Files
santa/Source/santad/SNTCompilerControllerTest.mm
Matt W fcb49701b3 ES and Logging Interfaces Redesign (#888)
* Initial structure for ES wrappers, enriched types, logging

* Basic working ES and logging functionality

* Add in oneTBB and thread-safe-lru deps

* Added a bunch of enriched types

* Auto-mute self when establishing ES client

* Basic auth, tamper client. Syslog of all events. Basic compiler tracking.

* Update copyright header blobs, convert some tabs to spaces

* Auth result cache. Fix getting translocation path.

* Added remaining cache methods

* Add AuthResultCache to Recorder client. Cache now operates on es_file_t.

* Hooked up SNTPrefixTree

* Fix CompilerController for RENAME. Fix AllowList logging missing path.

* Block loading Santa kext

* Added device manager client

* Properly log DiskAppear events

* Fix build to adopt new adhoc build

* Handle clearing cache on UNMOUNT events

* Ignore other ES clients if configured

* Remove SNTAllowlistInfo. Rename AllowList to Allowlist. Minor cleanup.

* Recorder now logs asynchronously. Enricher now returns shared_ptrs.

* Added File writer. Added timestamps to BasicStream serializer.

* Skip calling stat in SNTFileInfo when path given by ES.

* Fix build issue

* Address draft PR feedback

* santactl integrated, XPC works, fix file writer bug

* Integrate syncservice. Start observing some config changes.

* Add metrics service wrapper

* Add metrics config observers and metrics interval reset.

* Start better dependency control. Add Null logger support.

* Added more deps

* Added more deps

* Fix issue where metric service wasn't starting

* Add missing variant include

* Fix missing parent proc name

* Added googletest and new unit test macro

* Started expanding AuthResultCacheTest

* Properly mock EndpointSecurityAPI

* Finished AuthResultCacheTest

* bazelrc now builds all C++ as C++17. Added LoggerTest.

* Add FileTest. Abstract some File constants to Logger.

* Added Empty serializer test

* Started work on BasicStringTest. Fixed some BasicString serialization bugs.

* Added Unlink BasicString serialization test

* Added some more tests. Commonized some test code

* Finished BasicStringTest. Converted to XCTest.

* Standardize esapi variable naming

* Bubble up gTest expect failures to XCTest failures

* AuthResultCacheTest now uses XCTest. Added common TestUtils.h

* EmptyTest now uses XCTest.

* FileTest now uses XCTest

* LoggerTest now uses XCTest. Removed santa_unit_gtest bazel macro.

* Added ClientTest

* Add basic Enricher tests

* Add MessageTest. Make more TestUtils.

* Rename metrics to Metrics

* Add MetricsTest.

* Apply template pattern to Serializer

* Add SNTDecisionCacheTest.

* Add SNTCachedDecisionTest.

* Testing with coveralls debug mode

* Allow manual CI runs

* Remove unused property

* Started work on SNTEndpointSecurityClientTest.

* WIP SNTEndpointSecurityClientTest, fix test run issue

* Added more base ES client tests

* Add more base ES client tests

* Base ES client tests done. Added serializer utils/tests. Expanded basic string tests.

* Add utils test to test suite

* Add copy ctor. Add test output to bazel coverage.

* Single thread bazel coverage

* Updaload coverage file

* Updaload coverage file

* Old gen cov test

* Restructure message handlers to enable better testability

* Added enable tests for all ES clients

* Made a single MockEndpointSecurityAPI class to share everywhere

* Added most of SNTCompilerControllerTest

* Cleanup SNTCompilerControllerTest

* Started expanding Auth client test

* Finished up the Authorizer tests

* Move to using enum class for notify/auth instead of bool

* WIP for tamper resistance test. ASAN issues.

* Add OCMock patch to fix test issue on ARM Macs

* Changed patches directory name to external_patches

* Update WORKSPACE path

* Finished up Tamper Resistance tests

* Finished up Recorder tests.

* Move SNTExecutionControllerTest to ObjC++

* Initial work to port SNTExecutionControllerTest

* Finished porting SNTExecutionControllerTest.

* Added SNTExecutionControllerTest to list of unit tests

* Ported SNTEndpointSecurityDeviceManager.

* Test cleanup, use MockESAPI expectation helpers

* Verify SNTEndpointSecurityDeviceManager expectations differently

* Test cleanup, omit gTest param list where unused

* Log message cleanup

* Rename SNTApplicationTest to santad_test.mm

* Finished porting santad_test, formerly SNTApplicationTest

* Fix SNTEndpointSecurityDeviceManager issues

* Pulled in missed fixes. Updated tests.

* Renamed lowercase filenames to match rest of codebase

* Fix non-static dispatch_once_t, and noisy watching compiler log message

* WIP Started process of removing components no longer used

* WIP Continued process of removing components no longer used

* BUILD file cleanup. Proto warning. Removed unused global

* Rename SNTEventProvider to SNTEndpointSecurityEventHandler

* Rename SNTEndpointSecurityEventHandler protocol

* Remove EnableSysxCache option. Remove --quick flag used during dev.

* Ran testing/fix.sh

* Addmissing param to fix.sh that was omitting .mm files.

* clang-format

* Fix linter: find cmd missing .mm ext, git grep exclude patch files.

* Use MakeESProcess default params in tests

* Move variables to camelCase in objc classes

* More case changes

* Sanitize strings

* Change dispatch queue priorities and standardize daemon queue naming

* Exclude patch files in markdown check

* Ensure string log messages end with newline

* Fix BasicStringTest

* Disable clang-format in code producing different results in local/remote versions

* Moved to using date ranges in copyright notices as per current guidelines

* Update Source/common/SNTConfigurator.h

Suggestion adding whitespace in comment to fix clang-format mangling

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>

* Removed santa_panic macro used in one place

* Updated comment about ES cachability

* Pin oneTBB to specific commit

* Address outstanding WORKSPACE 'canonical reproducible form' messages

* Use string append instead of ostringstream due to benchmark results

* Remove use of freind classes in EnrichedTypes.h

* Added SNTKVOManager, removed observers from SNTConfigurator.

* Fixed SNTEndpointSecurityRecorderTest class name

* Reduce usage of the auto keyword

* Each SNTKVOManager instance now adds its own observer

* Replaced more auto keywords with real types.

* Remove leftover code coverage debugging from ci.yml

* Updated comment

* Memoize SNTFileInfo sha256. Reduce some cache sizes.

* Fix issue checking for translocated paths

* Use more performant NSURL creation method

* Fix lint issue

* Address PR feedback

* Use an array literal for kvo objects

* Fix some clang tidy and import issues

* Replace third party LRU cache with SantaCache for now

* Fix clang tidy issues

* Address PR feedback

* Fix comment typo

Co-authored-by: Pete Markowsky <pmarkowsky@users.noreply.github.com>

* Added todo for when we adopt macOS 13

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
Co-authored-by: Pete Markowsky <pmarkowsky@users.noreply.github.com>
2022-09-22 10:18:41 -04:00

255 lines
8.9 KiB
Plaintext

/// Copyright 2022 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include <EndpointSecurity/EndpointSecurity.h>
#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <cstdio>
#include <memory>
#include "Source/santad/SNTCompilerController.h"
#include <string_view>
#import "Source/common/SNTCachedDecision.h"
#import "Source/common/SNTFileInfo.h"
#include "Source/common/TestUtils.h"
#include "Source/santad/EventProviders/EndpointSecurity/Message.h"
#include "Source/santad/EventProviders/EndpointSecurity/MockEndpointSecurityAPI.h"
#include "Source/santad/Logs/EndpointSecurity/Logger.h"
#import "Source/santad/SNTCompilerController.h"
#import "Source/santad/SNTDecisionCache.h"
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::Logger;
static const pid_t PID_MAX = 99999;
@interface SNTCompilerController (Testing)
- (BOOL)isCompiler:(const audit_token_t &)tok;
- (void)saveFakeDecision:(const es_file_t *)esFile;
- (void)removeFakeDecision:(const es_file_t *)esFile;
- (void)createTransitiveRule:(const Message &)esMsg
target:(const es_file_t *)targetFile
logger:(std::shared_ptr<Logger>)logger;
@end
@interface SNTCompilerControllerTest : XCTestCase
@property id mockDecisionCache;
@property audit_token_t tok1;
@property audit_token_t tok2;
@property audit_token_t tokNegativePid;
@property audit_token_t tokLargePid;
@end
@implementation SNTCompilerControllerTest
- (void)setUp {
self.mockDecisionCache = OCMClassMock([SNTDecisionCache class]);
OCMStub([self.mockDecisionCache sharedCache]).andReturn(self.mockDecisionCache);
self.tok1 = MakeAuditToken(12, 11);
self.tok2 = MakeAuditToken(34, 22);
self.tokNegativePid = MakeAuditToken(-1, 33);
self.tokLargePid = MakeAuditToken(PID_MAX + 1, 44);
}
- (void)tearDown {
[self.mockDecisionCache stopMocking];
}
- (void)testIsCompiler {
SNTCompilerController *cc = [[SNTCompilerController alloc] init];
// Ensure invalid PIDs are handled
XCTAssertFalse([cc isCompiler:self.tokNegativePid]);
XCTAssertFalse([cc isCompiler:self.tokLargePid]);
// Items in the compiler control cache are initially false
XCTAssertFalse([cc isCompiler:self.tok1]);
// Start tracking a process as a compiler
[cc setProcess:self.tok1 isCompiler:true];
XCTAssertTrue([cc isCompiler:self.tok1]);
// Stop tracking a process as a compiler
[cc setProcess:self.tok1 isCompiler:false];
XCTAssertFalse([cc isCompiler:self.tok1]);
}
- (void)testSetProcessIsCompiler {
SNTCompilerController *cc = [[SNTCompilerController alloc] init];
// Ensure invalid PIDs are handled
XCTAssertNoThrow([cc setProcess:self.tokNegativePid isCompiler:true]);
XCTAssertNoThrow([cc setProcess:self.tokLargePid isCompiler:true]);
// Ensure test tokens are initially false
XCTAssertFalse([cc isCompiler:self.tok1]);
XCTAssertFalse([cc isCompiler:self.tok2]);
// Start tracking one of the toks
[cc setProcess:self.tok1 isCompiler:true];
XCTAssertTrue([cc isCompiler:self.tok1]);
XCTAssertFalse([cc isCompiler:self.tok2]);
// Start tracking both toks
[cc setProcess:self.tok2 isCompiler:true];
XCTAssertTrue([cc isCompiler:self.tok1]);
XCTAssertTrue([cc isCompiler:self.tok2]);
// Stop tracking one of the toks
[cc setProcess:self.tok1 isCompiler:false];
XCTAssertFalse([cc isCompiler:self.tok1]);
XCTAssertTrue([cc isCompiler:self.tok2]);
}
- (void)testSaveFakeDecision {
es_file_t file = MakeESFile("foo", {
.st_dev = 12,
.st_ino = 34,
});
OCMExpect([self.mockDecisionCache
cacheDecision:[OCMArg checkWithBlock:^BOOL(SNTCachedDecision *cd) {
return cd.vnodeId.fsid == file.stat.st_dev && cd.vnodeId.fileid == file.stat.st_ino &&
cd.decision == SNTEventStateAllowPendingTransitive &&
[cd.sha256 isEqualToString:@"pending"];
}]]);
SNTCompilerController *cc = [[SNTCompilerController alloc] init];
[cc saveFakeDecision:&file];
XCTAssertTrue(OCMVerifyAll(self.mockDecisionCache), "Unable to verify all expectations");
}
- (void)testRemoveFakeDecision {
es_file_t file = MakeESFile("foo", {
.st_dev = 12,
.st_ino = 34,
});
OCMExpect([self.mockDecisionCache forgetCachedDecisionForFile:file.stat]);
SNTCompilerController *cc = [[SNTCompilerController alloc] init];
[cc removeFakeDecision:&file];
XCTAssertTrue(OCMVerifyAll(self.mockDecisionCache), "Unable to verify all expectations");
}
- (void)testHandleEventWithLogger {
es_file_t file = MakeESFile("foo");
es_file_t ignoredFile = MakeESFile("/dev/bar");
es_file_t normalFile = MakeESFile("bar");
audit_token_t compilerTok = MakeAuditToken(12, 34);
audit_token_t notCompilerTok = MakeAuditToken(56, 78);
es_process_t compilerProc = MakeESProcess(&file, compilerTok, {});
es_process_t notCompilerProc = MakeESProcess(&file, notCompilerTok, {});
es_message_t esMsg;
auto mockESApi = std::make_shared<MockEndpointSecurityAPI>();
mockESApi->SetExpectationsRetainReleaseMessage(&esMsg);
SNTCompilerController *cc = [[SNTCompilerController alloc] init];
// Mark a process as a compiler for use with these tests.
[cc setProcess:compilerTok isCompiler:true];
// Ensure unhandled event types return appropriately
{
esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_FORK, &notCompilerProc);
Message msg(mockESApi, &esMsg);
XCTAssertFalse([cc handleEvent:msg withLogger:nullptr]);
}
// Ensure non-compiler process events return false
{
esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_CLOSE, &notCompilerProc);
Message msg(mockESApi, &esMsg);
XCTAssertFalse([cc handleEvent:msg withLogger:nullptr]);
}
{
esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_RENAME, &notCompilerProc);
Message msg(mockESApi, &esMsg);
XCTAssertFalse([cc handleEvent:msg withLogger:nullptr]);
}
// Ensure compiler process events are only handled with non-ignored paths
{
esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_CLOSE, &compilerProc);
esMsg.event.close.target = &ignoredFile;
Message msg(mockESApi, &esMsg);
XCTAssertFalse([cc handleEvent:msg withLogger:nullptr]);
}
{
esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_RENAME, &compilerProc);
esMsg.event.rename.source = &ignoredFile;
Message msg(mockESApi, &esMsg);
XCTAssertFalse([cc handleEvent:msg withLogger:nullptr]);
}
// Ensure EXIT events stop tracking the process as a compiler
{
esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_EXIT, &compilerProc);
Message msg(mockESApi, &esMsg);
id mockCompilerController = OCMPartialMock(cc);
OCMExpect([mockCompilerController setProcess:compilerProc.audit_token isCompiler:false]);
XCTAssertTrue([cc handleEvent:msg withLogger:nullptr]);
XCTAssertTrue(OCMVerifyAll(mockCompilerController), "Unable to verify all expectations");
[mockCompilerController stopMocking];
}
// Ensure transitive rules are created when the given event is handled
{
esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_CLOSE, &compilerProc);
esMsg.event.close.target = &normalFile;
Message msg(mockESApi, &esMsg);
id mockCompilerController = OCMPartialMock(cc);
OCMExpect([mockCompilerController createTransitiveRule:msg
target:esMsg.event.close.target
logger:nullptr])
.ignoringNonObjectArgs();
XCTAssertTrue([cc handleEvent:msg withLogger:nullptr]);
XCTAssertTrue(OCMVerifyAll(mockCompilerController), "Unable to verify all expectations");
[mockCompilerController stopMocking];
}
{
esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_RENAME, &compilerProc);
esMsg.event.rename.source = &normalFile;
Message msg(mockESApi, &esMsg);
id mockCompilerController = OCMPartialMock(cc);
OCMExpect([mockCompilerController createTransitiveRule:msg
target:esMsg.event.close.target
logger:nullptr])
.ignoringNonObjectArgs();
XCTAssertTrue([cc handleEvent:msg withLogger:nullptr]);
XCTAssertTrue(OCMVerifyAll(mockCompilerController), "Unable to verify all expectations");
[mockCompilerController stopMocking];
}
}
@end