Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd23a5c3b7 | ||
|
|
ec203e8796 | ||
|
|
57ff69208d | ||
|
|
f00b7d2ded | ||
|
|
9791fdd53c | ||
|
|
26e2203f1e | ||
|
|
4a47195d12 | ||
|
|
4436e221df | ||
|
|
deccc8a148 | ||
|
|
06da796a4d | ||
|
|
7b99a76d0d | ||
|
|
c2d3e99446 | ||
|
|
6db7fea8ae | ||
|
|
6fcb4cfe63 | ||
|
|
8b55ee4da5 | ||
|
|
cc3177502c | ||
|
|
a49a59b109 | ||
|
|
2c06c39c82 | ||
|
|
234f81ea7c | ||
|
|
743c567bf8 | ||
|
|
21220f1499 | ||
|
|
39f3ffe8fc | ||
|
|
fdb01928a0 | ||
|
|
fbefbc5910 | ||
|
|
9db00d143d | ||
|
|
1cc40d59d8 | ||
|
|
ba1ace56f0 | ||
|
|
6d911e9d6e | ||
|
|
7e2b291122 | ||
|
|
64096f5d08 | ||
|
|
aec1c74fab | ||
|
|
d4a0d77cb9 | ||
|
|
7df209ed3f | ||
|
|
b7421e4499 | ||
|
|
e044fe3601 |
@@ -1,13 +1,14 @@
|
||||
name: Check Markdown links
|
||||
name: Check Markdown
|
||||
|
||||
on:
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.md"
|
||||
|
||||
jobs:
|
||||
markdown-link-check:
|
||||
markdown-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||
- run: "! git grep -EIn $'[ \t]+$'"
|
||||
52
.github/workflows/ci.yml
vendored
@@ -1,66 +1,36 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
paths:
|
||||
- 'Source/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'Source/**'
|
||||
|
||||
jobs:
|
||||
preqs:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
run_build_and_tests: ${{ steps.step1.outputs.run_build_and_tests }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Check If We Need to Run Build/Test
|
||||
id: step1
|
||||
run: |
|
||||
git remote add mainline https://github.com/google/santa.git
|
||||
git fetch mainline main
|
||||
git diff --name-only mainline/main HEAD > files.txt
|
||||
echo "FILES CHANGED: $(wc -l ./files.txt)\n"
|
||||
|
||||
cat files.txt
|
||||
|
||||
build_and_run_tests=0
|
||||
|
||||
for file in `cat files.txt`; do
|
||||
if [[ $file = Source/* ]]; then
|
||||
build_and_run_test=1;
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $build_and_run_test != 0 ]]; then
|
||||
echo "NEED TO RUN BUILD AND TESTS"
|
||||
echo "::set-output name=run_build_and_tests::true"
|
||||
else
|
||||
echo "::set-output name=run_build_and_tests::false"
|
||||
fi
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [preqs]
|
||||
if: needs.preqs.outputs.run_build_and_tests == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run linters
|
||||
run: ./Testing/lint.sh
|
||||
|
||||
|
||||
build_userspace:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-10.15, macos-11, macos-12]
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [preqs]
|
||||
if: needs.preqs.outputs.run_build_and_tests == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build Userspace
|
||||
run: bazel build --apple_generate_dsym -c opt :release --define=SANTA_BUILD_TYPE=ci
|
||||
run: bazel build --apple_generate_dsym -c opt :release --define=SANTA_BUILD_TYPE=adhoc
|
||||
|
||||
unit_tests:
|
||||
strategy:
|
||||
@@ -68,17 +38,13 @@ jobs:
|
||||
matrix:
|
||||
os: [macos-10.15, macos-11, macos-12]
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [preqs]
|
||||
if: needs.preqs.outputs.run_build_and_tests == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run All Tests
|
||||
run: bazel test :unit_tests --define=SANTA_BUILD_TYPE=ci --test_output=errors
|
||||
run: bazel test :unit_tests --define=SANTA_BUILD_TYPE=adhoc --test_output=errors
|
||||
|
||||
test_coverage:
|
||||
runs-on: macos-11
|
||||
needs: [preqs]
|
||||
if: needs.preqs.outputs.run_build_and_tests == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Generate test coverage
|
||||
@@ -92,8 +58,6 @@ jobs:
|
||||
|
||||
benchmark:
|
||||
runs-on: macos-11
|
||||
needs: [preqs]
|
||||
if: needs.preqs.outputs.run_build_and_tests == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run All Tests
|
||||
|
||||
2
.github/workflows/continuous.yml
vendored
@@ -10,4 +10,4 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checks for flaky tests
|
||||
run: bazel test --test_strategy=exclusive --test_output=errors --runs_per_test 50 -t- :unit_tests --define=SANTA_BUILD_TYPE=ci
|
||||
run: bazel test --test_strategy=exclusive --test_output=errors --runs_per_test 50 -t- :unit_tests --define=SANTA_BUILD_TYPE=adhoc
|
||||
|
||||
14
BUILD
@@ -27,10 +27,11 @@ config_setting(
|
||||
visibility = [":santa_package_group"],
|
||||
)
|
||||
|
||||
# Used to detect CI builds
|
||||
# Adhoc signed - provisioning profiles are not used.
|
||||
# Used for CI runs and dev builds when SIP is disabled.
|
||||
config_setting(
|
||||
name = "ci_build",
|
||||
values = {"define": "SANTA_BUILD_TYPE=ci"},
|
||||
name = "adhoc_build",
|
||||
values = {"define": "SANTA_BUILD_TYPE=adhoc"},
|
||||
visibility = [":santa_package_group"],
|
||||
)
|
||||
|
||||
@@ -73,14 +74,14 @@ launchctl load /Library/LaunchAgents/com.google.santa.plist
|
||||
run_command(
|
||||
name = "reload",
|
||||
srcs = [
|
||||
"//Source/santa:Santa",
|
||||
"//Source/gui:Santa",
|
||||
],
|
||||
cmd = """
|
||||
set -e
|
||||
|
||||
rm -rf /tmp/bazel_santa_reload
|
||||
unzip -d /tmp/bazel_santa_reload \
|
||||
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*$(COMPILATION_MODE)*/bin/Source/santa/Santa.zip >/dev/null
|
||||
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*$(COMPILATION_MODE)*/bin/Source/gui/Santa.zip >/dev/null
|
||||
echo "You may be asked for your password for sudo"
|
||||
sudo BINARIES=/tmp/bazel_santa_reload CONF=$${BUILD_WORKSPACE_DIRECTORY}/Conf \
|
||||
$${BUILD_WORKSPACE_DIRECTORY}/Conf/install.sh
|
||||
@@ -95,7 +96,7 @@ echo "Time to stop being naughty"
|
||||
genrule(
|
||||
name = "release",
|
||||
srcs = [
|
||||
"//Source/santa:Santa",
|
||||
"//Source/gui:Santa",
|
||||
"Conf/install.sh",
|
||||
"Conf/uninstall.sh",
|
||||
"Conf/com.google.santa.bundleservice.plist",
|
||||
@@ -190,6 +191,7 @@ test_suite(
|
||||
name = "unit_tests",
|
||||
tests = [
|
||||
"//Source/common:unit_tests",
|
||||
"//Source/gui:unit_tests",
|
||||
"//Source/santactl:unit_tests",
|
||||
"//Source/santad:unit_tests",
|
||||
"//Source/santametricservice:unit_tests",
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
<img src="https://raw.githubusercontent.com/google/santa/main/Source/santa/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
|
||||
</p>
|
||||
|
||||
Santa is a binary authorization system for macOS. It consists of a system
|
||||
extension that monitors for executions, a daemon that makes execution decisions
|
||||
Santa is a binary authorization system for macOS. It consists of a system
|
||||
extension that monitors for executions, a daemon that makes execution decisions
|
||||
based on the contents of a local database, a GUI agent that notifies the user in
|
||||
case of a block decision and a command-line utility for managing the system and
|
||||
case of a block decision and a command-line utility for managing the system and
|
||||
synchronizing the database with a server.
|
||||
|
||||
It is named Santa because it keeps track of binaries that are naughty or nice.
|
||||
@@ -16,7 +16,7 @@ It is named Santa because it keeps track of binaries that are naughty or nice.
|
||||
|
||||
The Santa docs are stored in the
|
||||
[Docs](https://github.com/google/santa/blob/main/docs) directory and published
|
||||
at http://santa.dev.
|
||||
at https://santa.dev.
|
||||
|
||||
The docs include deployment options, details on how parts of Santa work and
|
||||
instructions for developing Santa itself.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Reporting a Vulnerability
|
||||
|
||||
If you believe you have found a security vulnerability, we would appreciate private disclosure
|
||||
so that we can work on a fix before disclosure. Any vulnerabilities reported to us will be
|
||||
so that we can work on a fix before disclosure. Any vulnerabilities reported to us will be
|
||||
disclosed publicly either when a new version with fixes is released or 90 days has passed,
|
||||
whichever comes first.
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ objc_library(
|
||||
hdrs = ["SNTConfigurator.h"],
|
||||
deps = [
|
||||
":SNTCommonEnums",
|
||||
":SNTRule",
|
||||
":SNTStrengthify",
|
||||
":SNTSystemInfo",
|
||||
],
|
||||
@@ -149,7 +150,16 @@ objc_library(
|
||||
name = "SNTRule",
|
||||
srcs = ["SNTRule.m"],
|
||||
hdrs = ["SNTRule.h"],
|
||||
deps = [":SNTCommonEnums"],
|
||||
deps = [
|
||||
":SNTCommonEnums",
|
||||
":SNTSyncConstants",
|
||||
],
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
name = "SNTRuleTest",
|
||||
srcs = ["SNTRuleTest.m"],
|
||||
deps = [":SNTRule"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
@@ -167,6 +177,12 @@ cc_library(
|
||||
hdrs = ["SNTStrengthify.h"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTSyncConstants",
|
||||
srcs = ["SNTSyncConstants.m"],
|
||||
hdrs = ["SNTSyncConstants.h"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTSystemInfo",
|
||||
srcs = ["SNTSystemInfo.m"],
|
||||
@@ -197,6 +213,10 @@ objc_library(
|
||||
name = "SNTXPCControlInterface",
|
||||
srcs = ["SNTXPCControlInterface.m"],
|
||||
hdrs = ["SNTXPCControlInterface.h"],
|
||||
defines = select({
|
||||
"//:adhoc_build": ["SANTAADHOC"],
|
||||
"//conditions:default": None,
|
||||
}),
|
||||
deps = [
|
||||
":SNTCommonEnums",
|
||||
":SNTConfigurator",
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
|
||||
@class SNTRule;
|
||||
|
||||
///
|
||||
/// Singleton that provides an interface for managing configuration values on disk
|
||||
/// @note This class is designed as a singleton but that is not strictly enforced.
|
||||
@@ -46,6 +48,32 @@
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL failClosed;
|
||||
|
||||
///
|
||||
/// A set of static rules that should always apply. These can be used as a
|
||||
/// fallback set of rules for management tools that should always be allowed to
|
||||
/// run even if a sync server does something unexpected. It can also be used
|
||||
/// as the sole source of rules, distributed with an MDM.
|
||||
///
|
||||
/// The value of this key should be an array containing dictionaries. Each
|
||||
/// dictionary should contain the same keys used for syncing, e.g:
|
||||
///
|
||||
/// <key>StaticRules</key>
|
||||
/// <array>
|
||||
/// <dict>
|
||||
/// <key>identifier</key>
|
||||
/// <string>binary sha256, certificate sha256, team ID</string>
|
||||
/// <key>rule_type</key>
|
||||
/// <string>BINARY</string> (one of BINARY, CERTIFICATE or TEAMID)
|
||||
/// <key>policy</key>
|
||||
/// <string>BLOCKLIST</string> (one of ALLOWLIST, ALLOWLIST_COMPILER, BLOCKLIST, SILENT_BLOCKLIST)
|
||||
/// </dict>
|
||||
/// </array>
|
||||
///
|
||||
/// The return of this property is a dictionary where the keys are the
|
||||
/// identifiers of each rule, with the SNTRule as a value
|
||||
///
|
||||
@property(readonly, nonatomic) NSDictionary<NSString *, SNTRule *> *staticRules;
|
||||
|
||||
///
|
||||
/// The regex of allowed paths. Regexes are specified in ICU format.
|
||||
///
|
||||
@@ -227,6 +255,16 @@
|
||||
|
||||
#pragma mark - GUI Settings
|
||||
|
||||
///
|
||||
/// When silent mode is enabled, Santa will never show notifications for
|
||||
/// blocked processes.
|
||||
///
|
||||
/// This can be a very confusing experience for users, use with caution.
|
||||
///
|
||||
/// Defaults to NO.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableSilentMode;
|
||||
|
||||
///
|
||||
/// The text to display when opening Santa.app.
|
||||
/// If unset, the default text will be displayed.
|
||||
@@ -423,6 +461,11 @@
|
||||
///
|
||||
@property(nonatomic) BOOL enableAllEventUpload;
|
||||
|
||||
///
|
||||
/// If true, events will *not* be uploaded for ALLOW_UNKNOWN events for clients in Monitor mode.
|
||||
///
|
||||
@property(nonatomic) BOOL disableUnknownEventUpload;
|
||||
|
||||
///
|
||||
/// If true, forks and exits will be logged. Defaults to false.
|
||||
///
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#import "Source/common/SNTRule.h"
|
||||
#import "Source/common/SNTStrengthify.h"
|
||||
#import "Source/common/SNTSystemInfo.h"
|
||||
|
||||
@@ -33,6 +34,9 @@
|
||||
|
||||
/// Was --debug passed as an argument to this process?
|
||||
@property(readonly, nonatomic) BOOL debugFlag;
|
||||
|
||||
/// Holds the last processed hash of the static rules list.
|
||||
@property(atomic) NSDictionary *cachedStaticRules;
|
||||
@end
|
||||
|
||||
@implementation SNTConfigurator
|
||||
@@ -44,6 +48,7 @@ NSString *const kSyncStateFilePath = @"/var/db/santa/sync-state.plist";
|
||||
static NSString *const kMobileConfigDomain = @"com.google.santa";
|
||||
|
||||
/// The keys managed by a mobileconfig.
|
||||
static NSString *const kStaticRules = @"StaticRules";
|
||||
static NSString *const kSyncBaseURLKey = @"SyncBaseURL";
|
||||
static NSString *const kSyncProxyConfigKey = @"SyncProxyConfiguration";
|
||||
static NSString *const kSyncEnableCleanSyncEventUpload = @"SyncEnableCleanSyncEventUpload";
|
||||
@@ -61,7 +66,8 @@ static NSString *const kMachineOwnerPlistKeyKey = @"MachineOwnerKey";
|
||||
static NSString *const kMachineIDPlistFileKey = @"MachineIDPlist";
|
||||
static NSString *const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
|
||||
static NSString *const kAboutText = @"AboutText";
|
||||
static NSString *const kEnableSilentModeKey = @"EnableSilentMode";
|
||||
static NSString *const kAboutTextKey = @"AboutText";
|
||||
static NSString *const kMoreInfoURLKey = @"MoreInfoURL";
|
||||
static NSString *const kEventDetailURLKey = @"EventDetailURL";
|
||||
static NSString *const kEventDetailTextKey = @"EventDetailText";
|
||||
@@ -113,6 +119,7 @@ static NSString *const kAllowedPathRegexKeyDeprecated = @"WhitelistRegex";
|
||||
static NSString *const kBlockedPathRegexKey = @"BlockedPathRegex";
|
||||
static NSString *const kBlockedPathRegexKeyDeprecated = @"BlacklistRegex";
|
||||
static NSString *const kEnableAllEventUploadKey = @"EnableAllEventUpload";
|
||||
static NSString *const kDisableUnknownEventUploadKey = @"DisableUnknownEventUpload";
|
||||
|
||||
// TODO(markowsky): move these to sync server only.
|
||||
static NSString *const kMetricFormat = @"MetricFormat";
|
||||
@@ -166,7 +173,8 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
kRemountUSBModeKey : array,
|
||||
kEnablePageZeroProtectionKey : number,
|
||||
kEnableBadSignatureProtectionKey : number,
|
||||
kAboutText : string,
|
||||
kEnableSilentModeKey : string,
|
||||
kAboutTextKey : string,
|
||||
kMoreInfoURLKey : string,
|
||||
kEventDetailURLKey : string,
|
||||
kEventDetailTextKey : string,
|
||||
@@ -176,6 +184,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
kRemountUSBBlockMessage : string,
|
||||
kModeNotificationMonitor : string,
|
||||
kModeNotificationLockdown : string,
|
||||
kStaticRules : array,
|
||||
kSyncBaseURLKey : string,
|
||||
kSyncProxyConfigKey : dictionary,
|
||||
kClientAuthCertificateFileKey : string,
|
||||
@@ -211,10 +220,12 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
kMetricExportTimeout : number,
|
||||
kMetricExtraLabels : dictionary,
|
||||
kEnableAllEventUploadKey : number,
|
||||
kDisableUnknownEventUploadKey : number,
|
||||
};
|
||||
_defaults = [NSUserDefaults standardUserDefaults];
|
||||
[_defaults addSuiteNamed:@"com.google.santa"];
|
||||
_configState = [self readForcedConfig];
|
||||
[self cacheStaticRules];
|
||||
_syncState = [self readSyncStateFromDisk] ?: [NSMutableDictionary dictionary];
|
||||
_debugFlag = [[NSProcessInfo processInfo].arguments containsObject:@"--debug"];
|
||||
[self startWatchingDefaults];
|
||||
@@ -282,6 +293,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingStaticRules {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSyncBaseURL {
|
||||
return [self configStateSet];
|
||||
}
|
||||
@@ -290,6 +305,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableSilentMode {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingAboutText {
|
||||
return [self configStateSet];
|
||||
}
|
||||
@@ -402,6 +421,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self syncAndConfigStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingDisableUnknownEventUpload {
|
||||
return [self syncAndConfigStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableSysxCache {
|
||||
return [self configStateSet];
|
||||
}
|
||||
@@ -442,6 +465,26 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingBlockUSBMount {
|
||||
return [self syncAndConfigStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingBannedUSBBlockMessage {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingRemountUSBMode {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingRemountUSBBlockMessage {
|
||||
return [self syncAndConfigStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingUsbBlockMessage {
|
||||
return [self syncAndConfigStateSet];
|
||||
}
|
||||
|
||||
#pragma mark Public Interface
|
||||
|
||||
- (SNTClientMode)clientMode {
|
||||
@@ -549,6 +592,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return args;
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, SNTRule *> *)staticRules {
|
||||
return self.cachedStaticRules;
|
||||
}
|
||||
|
||||
- (NSURL *)syncBaseURL {
|
||||
NSString *urlString = self.configState[kSyncBaseURLKey];
|
||||
if (![urlString hasSuffix:@"/"]) urlString = [urlString stringByAppendingString:@"/"];
|
||||
@@ -570,8 +617,13 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (BOOL)enableSilentMode {
|
||||
NSNumber *number = self.configState[kEnableSilentModeKey];
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (NSString *)aboutText {
|
||||
return self.configState[kAboutText];
|
||||
return self.configState[kAboutTextKey];
|
||||
}
|
||||
|
||||
- (NSURL *)moreInfoURL {
|
||||
@@ -762,6 +814,17 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
[self updateSyncStateForKey:kEnableAllEventUploadKey value:@(enabled)];
|
||||
}
|
||||
|
||||
- (BOOL)disableUnknownEventUpload {
|
||||
NSNumber *n = self.syncState[kDisableUnknownEventUploadKey];
|
||||
if (n) return [n boolValue];
|
||||
|
||||
return [self.configState[kDisableUnknownEventUploadKey] boolValue];
|
||||
}
|
||||
|
||||
- (void)setDisableUnknownEventUpload:(BOOL)enabled {
|
||||
[self updateSyncStateForKey:kDisableUnknownEventUploadKey value:@(enabled)];
|
||||
}
|
||||
|
||||
- (BOOL)enableForkAndExitLogging {
|
||||
NSNumber *number = self.configState[kEnableForkAndExitLogging];
|
||||
return number ? [number boolValue] : NO;
|
||||
@@ -907,7 +970,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
syncState[kAllowedPathRegexKey] = [syncState[kAllowedPathRegexKey] pattern];
|
||||
syncState[kBlockedPathRegexKey] = [syncState[kBlockedPathRegexKey] pattern];
|
||||
[syncState writeToFile:kSyncStateFilePath atomically:YES];
|
||||
[[NSFileManager defaultManager] setAttributes:@{NSFilePosixPermissions : @0644}
|
||||
[[NSFileManager defaultManager] setAttributes:@{NSFilePosixPermissions : @0600}
|
||||
ofItemAtPath:kSyncStateFilePath
|
||||
error:NULL];
|
||||
}
|
||||
@@ -964,6 +1027,24 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
///
|
||||
- (void)handleChange {
|
||||
self.configState = [self readForcedConfig];
|
||||
[self cacheStaticRules];
|
||||
}
|
||||
|
||||
///
|
||||
/// Processes the StaticRules key to create SNTRule objects and caches them for quick use
|
||||
///
|
||||
- (void)cacheStaticRules {
|
||||
NSArray *staticRules = self.configState[kStaticRules];
|
||||
if (![staticRules isKindOfClass:[NSArray class]]) return;
|
||||
|
||||
NSMutableDictionary<NSString *, SNTRule *> *rules =
|
||||
[NSMutableDictionary dictionaryWithCapacity:staticRules.count];
|
||||
for (id rule in staticRules) {
|
||||
if (![rule isKindOfClass:[NSDictionary class]]) return;
|
||||
SNTRule *r = [[SNTRule alloc] initWithDictionary:rule];
|
||||
rules[r.identifier] = r;
|
||||
}
|
||||
self.cachedStaticRules = [rules copy];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -108,6 +108,11 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType);
|
||||
*/
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/**
|
||||
* Resets all the metrics in this set. Intended only for testing.
|
||||
*/
|
||||
- (void)reset;
|
||||
|
||||
/**
|
||||
* Add a root label to the MetricSet.
|
||||
*/
|
||||
|
||||
@@ -485,6 +485,10 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
_metrics = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
- (void)addRootLabel:(NSString *)label value:(NSString *)value {
|
||||
@synchronized(self) {
|
||||
_rootLabels[label] = value;
|
||||
|
||||
@@ -64,6 +64,11 @@
|
||||
type:(SNTRuleType)type
|
||||
customMsg:(NSString *)customMsg;
|
||||
|
||||
///
|
||||
/// Initialize with a dictionary received from a sync server.
|
||||
///
|
||||
- (instancetype)initWithDictionary:(NSDictionary *)dict;
|
||||
|
||||
///
|
||||
/// Sets timestamp of rule to the current time.
|
||||
///
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/common/SNTRule.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
|
||||
@interface SNTRule ()
|
||||
@property(readwrite) NSUInteger timestamp;
|
||||
@@ -48,6 +49,60 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
// Converts rule information downloaded from the server into a SNTRule. Because any information
|
||||
// not recorded by SNTRule is thrown away here, this method is also responsible for dealing with
|
||||
// the extra bundle rule information (bundle_hash & rule_count).
|
||||
- (instancetype)initWithDictionary:(NSDictionary *)dict {
|
||||
if (![dict isKindOfClass:[NSDictionary class]]) return nil;
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_identifier = dict[kRuleIdentifier];
|
||||
if (![_identifier isKindOfClass:[NSString class]] || !_identifier.length) {
|
||||
_identifier = dict[kRuleSHA256];
|
||||
}
|
||||
if (![_identifier isKindOfClass:[NSString class]] || !_identifier.length) return nil;
|
||||
|
||||
NSString *policyString = dict[kRulePolicy];
|
||||
if (![policyString isKindOfClass:[NSString class]]) return nil;
|
||||
if ([policyString isEqual:kRulePolicyAllowlist] ||
|
||||
[policyString isEqual:kRulePolicyAllowlistDeprecated]) {
|
||||
_state = SNTRuleStateAllow;
|
||||
} else if ([policyString isEqual:kRulePolicyAllowlistCompiler] ||
|
||||
[policyString isEqual:kRulePolicyAllowlistCompilerDeprecated]) {
|
||||
_state = SNTRuleStateAllowCompiler;
|
||||
} else if ([policyString isEqual:kRulePolicyBlocklist] ||
|
||||
[policyString isEqual:kRulePolicyBlocklistDeprecated]) {
|
||||
_state = SNTRuleStateBlock;
|
||||
} else if ([policyString isEqual:kRulePolicySilentBlocklist] ||
|
||||
[policyString isEqual:kRulePolicySilentBlocklistDeprecated]) {
|
||||
_state = SNTRuleStateSilentBlock;
|
||||
} else if ([policyString isEqual:kRulePolicyRemove]) {
|
||||
_state = SNTRuleStateRemove;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *ruleTypeString = dict[kRuleType];
|
||||
if (![ruleTypeString isKindOfClass:[NSString class]]) return nil;
|
||||
if ([ruleTypeString isEqual:kRuleTypeBinary]) {
|
||||
_type = SNTRuleTypeBinary;
|
||||
} else if ([ruleTypeString isEqual:kRuleTypeCertificate]) {
|
||||
_type = SNTRuleTypeCertificate;
|
||||
} else if ([ruleTypeString isEqual:kRuleTypeTeamID]) {
|
||||
_type = SNTRuleTypeTeamID;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *customMsg = dict[kRuleCustomMsg];
|
||||
if ([customMsg isKindOfClass:[NSString class]] && customMsg.length) {
|
||||
_customMsg = customMsg;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark NSSecureCoding
|
||||
|
||||
#pragma clang diagnostic push
|
||||
|
||||
116
Source/common/SNTRuleTest.m
Normal file
@@ -0,0 +1,116 @@
|
||||
/// 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.
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "Source/common/SNTRule.h"
|
||||
|
||||
@interface SNTRuleTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation SNTRuleTest
|
||||
|
||||
- (void)testInitWithDictionaryValid {
|
||||
SNTRule *sut;
|
||||
|
||||
sut = [[SNTRule alloc] initWithDictionary:@{
|
||||
@"identifier" : @"some-sort-of-identifier",
|
||||
@"policy" : @"ALLOWLIST",
|
||||
@"rule_type" : @"BINARY",
|
||||
}];
|
||||
XCTAssertNotNil(sut);
|
||||
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
|
||||
XCTAssertEqual(sut.type, SNTRuleTypeBinary);
|
||||
XCTAssertEqual(sut.state, SNTRuleStateAllow);
|
||||
|
||||
sut = [[SNTRule alloc] initWithDictionary:@{
|
||||
@"sha256" : @"some-sort-of-identifier",
|
||||
@"policy" : @"BLOCKLIST",
|
||||
@"rule_type" : @"CERTIFICATE",
|
||||
}];
|
||||
XCTAssertNotNil(sut);
|
||||
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
|
||||
XCTAssertEqual(sut.type, SNTRuleTypeCertificate);
|
||||
XCTAssertEqual(sut.state, SNTRuleStateBlock);
|
||||
|
||||
sut = [[SNTRule alloc] initWithDictionary:@{
|
||||
@"identifier" : @"some-sort-of-identifier",
|
||||
@"policy" : @"SILENT_BLOCKLIST",
|
||||
@"rule_type" : @"TEAMID",
|
||||
}];
|
||||
XCTAssertNotNil(sut);
|
||||
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
|
||||
XCTAssertEqual(sut.type, SNTRuleTypeTeamID);
|
||||
XCTAssertEqual(sut.state, SNTRuleStateSilentBlock);
|
||||
|
||||
sut = [[SNTRule alloc] initWithDictionary:@{
|
||||
@"identifier" : @"some-sort-of-identifier",
|
||||
@"policy" : @"ALLOWLIST_COMPILER",
|
||||
@"rule_type" : @"BINARY",
|
||||
}];
|
||||
XCTAssertNotNil(sut);
|
||||
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
|
||||
XCTAssertEqual(sut.type, SNTRuleTypeBinary);
|
||||
XCTAssertEqual(sut.state, SNTRuleStateAllowCompiler);
|
||||
|
||||
sut = [[SNTRule alloc] initWithDictionary:@{
|
||||
@"identifier" : @"some-sort-of-identifier",
|
||||
@"policy" : @"REMOVE",
|
||||
@"rule_type" : @"TEAMID",
|
||||
}];
|
||||
XCTAssertNotNil(sut);
|
||||
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
|
||||
XCTAssertEqual(sut.type, SNTRuleTypeTeamID);
|
||||
XCTAssertEqual(sut.state, SNTRuleStateRemove);
|
||||
|
||||
sut = [[SNTRule alloc] initWithDictionary:@{
|
||||
@"identifier" : @"some-sort-of-identifier",
|
||||
@"policy" : @"ALLOWLIST",
|
||||
@"rule_type" : @"TEAMID",
|
||||
@"custom_msg" : @"A custom block message",
|
||||
}];
|
||||
XCTAssertNotNil(sut);
|
||||
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
|
||||
XCTAssertEqual(sut.type, SNTRuleTypeTeamID);
|
||||
XCTAssertEqual(sut.state, SNTRuleStateAllow);
|
||||
XCTAssertEqualObjects(sut.customMsg, @"A custom block message");
|
||||
}
|
||||
|
||||
- (void)testInitWithDictionaryInvalid {
|
||||
SNTRule *sut;
|
||||
|
||||
sut = [[SNTRule alloc] initWithDictionary:@{}];
|
||||
XCTAssertNil(sut);
|
||||
|
||||
sut = [[SNTRule alloc] initWithDictionary:@{
|
||||
@"identifier" : @"an-identifier",
|
||||
}];
|
||||
XCTAssertNil(sut);
|
||||
|
||||
sut = [[SNTRule alloc] initWithDictionary:@{
|
||||
@"identifier" : @"an-identifier",
|
||||
@"policy" : @"OTHERPOLICY",
|
||||
@"rule_type" : @"BINARY",
|
||||
}];
|
||||
XCTAssertNil(sut);
|
||||
|
||||
sut = [[SNTRule alloc] initWithDictionary:@{
|
||||
@"identifier" : @"an-identifier",
|
||||
@"policy" : @"ALLOWLIST",
|
||||
@"rule_type" : @"OTHER_RULE_TYPE",
|
||||
}];
|
||||
XCTAssertNil(sut);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -95,6 +95,12 @@
|
||||
///
|
||||
@property NSArray *signingChain;
|
||||
|
||||
|
||||
///
|
||||
/// If the executed file was signed, this is the Team ID if present in the signature information.
|
||||
///
|
||||
@property NSString *teamID;
|
||||
|
||||
///
|
||||
/// The user who executed the binary.
|
||||
///
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
ENCODE(self.fileBundleVersionString, @"fileBundleVersionString");
|
||||
|
||||
ENCODE(self.signingChain, @"signingChain");
|
||||
ENCODE(self.teamID, @"teamID");
|
||||
|
||||
ENCODE(self.executingUser, @"executingUser");
|
||||
ENCODE(self.occurrenceDate, @"occurrenceDate");
|
||||
@@ -93,6 +94,7 @@
|
||||
_fileBundleVersionString = DECODE(NSString, @"fileBundleVersionString");
|
||||
|
||||
_signingChain = DECODEARRAY(MOLCertificate, @"signingChain");
|
||||
_teamID = DECODE(NSString, @"teamID");
|
||||
|
||||
_executingUser = DECODE(NSString, @"executingUser");
|
||||
_occurrenceDate = DECODE(NSDate, @"occurrenceDate");
|
||||
|
||||
@@ -51,6 +51,7 @@ extern NSString *const kEnableTransitiveRules;
|
||||
extern NSString *const kEnableTransitiveRulesDeprecated;
|
||||
extern NSString *const kEnableTransitiveRulesSuperDeprecated;
|
||||
extern NSString *const kEnableAllEventUpload;
|
||||
extern NSString *const kDisableUnknownEventUpload;
|
||||
|
||||
extern NSString *const kEvents;
|
||||
extern NSString *const kFileSHA256;
|
||||
@@ -92,6 +93,7 @@ extern NSString *const kCertOrg;
|
||||
extern NSString *const kCertOU;
|
||||
extern NSString *const kCertValidFrom;
|
||||
extern NSString *const kCertValidUntil;
|
||||
extern NSString *const kTeamID;
|
||||
extern NSString *const kQuarantineDataURL;
|
||||
extern NSString *const kQuarantineRefererURL;
|
||||
extern NSString *const kQuarantineTimestamp;
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTSyncConstants.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
|
||||
NSString *const kXSRFToken = @"X-XSRF-TOKEN";
|
||||
|
||||
@@ -52,6 +52,7 @@ NSString *const kEnableTransitiveRules = @"enable_transitive_rules";
|
||||
NSString *const kEnableTransitiveRulesDeprecated = @"enabled_transitive_whitelisting";
|
||||
NSString *const kEnableTransitiveRulesSuperDeprecated = @"transitive_whitelisting_enabled";
|
||||
NSString *const kEnableAllEventUpload = @"enable_all_event_upload";
|
||||
NSString *const kDisableUnknownEventUpload = @"disable_unknown_event_upload";
|
||||
|
||||
NSString *const kEvents = @"events";
|
||||
NSString *const kFileSHA256 = @"file_sha256";
|
||||
@@ -93,6 +94,7 @@ NSString *const kCertOrg = @"org";
|
||||
NSString *const kCertOU = @"ou";
|
||||
NSString *const kCertValidFrom = @"valid_from";
|
||||
NSString *const kCertValidUntil = @"valid_until";
|
||||
NSString *const kTeamID = @"team_id";
|
||||
NSString *const kQuarantineDataURL = @"quarantine_data_url";
|
||||
NSString *const kQuarantineRefererURL = @"quarantine_referer_url";
|
||||
NSString *const kQuarantineTimestamp = @"quarantine_timestamp";
|
||||
@@ -20,7 +20,7 @@
|
||||
@protocol SNTDaemonControlXPC <SNTUnprivilegedDaemonControlXPC>
|
||||
|
||||
///
|
||||
/// Kernel ops
|
||||
/// Cache ops
|
||||
///
|
||||
- (void)flushCache:(void (^)(BOOL))reply;
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
/// Config ops
|
||||
///
|
||||
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)(void))reply;
|
||||
- (void)setXsrfToken:(NSString *)token reply:(void (^)(void))reply;
|
||||
- (void)setFullSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
|
||||
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
|
||||
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)(void))reply;
|
||||
@@ -52,6 +51,7 @@
|
||||
- (void)setEnableBundles:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
|
||||
- (void)setEnableTransitiveRules:(BOOL)enabled reply:(void (^)(void))reply;
|
||||
- (void)setEnableAllEventUpload:(BOOL)enabled reply:(void (^)(void))reply;
|
||||
- (void)setDisableUnknownEventUpload:(BOOL)enabled reply:(void (^)(void))reply;
|
||||
|
||||
///
|
||||
/// Syncd Ops
|
||||
|
||||
@@ -27,10 +27,16 @@ NSString *const kBundleID = @"com.google.santa.daemon";
|
||||
@implementation SNTXPCControlInterface
|
||||
|
||||
+ (NSString *)serviceID {
|
||||
#ifdef SANTAADHOC
|
||||
// The mach service for an adhoc signed ES sysx uses the "endpoint-security" prefix instead of
|
||||
// the teamid. In Santa's case it will be endpoint-security.com.google.santa.daemon.xpc.
|
||||
return [NSString stringWithFormat:@"endpoint-security.%@.xpc", kBundleID];
|
||||
#else
|
||||
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithSelf];
|
||||
// "teamid.com.google.santa.daemon.xpc"
|
||||
NSString *t = cs.signingInformation[@"teamid"];
|
||||
return [NSString stringWithFormat:@"%@.%@.xpc", t, kBundleID];
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (NSString *)systemExtensionID {
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate, int64_t compiler,
|
||||
int64_t transitive, int64_t teamID))reply;
|
||||
- (void)databaseEventCount:(void (^)(int64_t count))reply;
|
||||
- (void)staticRuleCount:(void (^)(int64_t count))reply;
|
||||
|
||||
///
|
||||
/// Decision ops
|
||||
@@ -63,7 +64,6 @@
|
||||
/// Config ops
|
||||
///
|
||||
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
|
||||
- (void)xsrfToken:(void (^)(NSString *))reply;
|
||||
- (void)clientMode:(void (^)(SNTClientMode))reply;
|
||||
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
|
||||
@@ -79,6 +79,7 @@ message Execution {
|
||||
optional string original_path = 11;
|
||||
repeated string args = 12;
|
||||
optional string machine_id = 13;
|
||||
optional string team_id = 14;
|
||||
}
|
||||
|
||||
message DiskAppeared {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_application")
|
||||
load("//:helper.bzl", "santa_unit_test")
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
@@ -31,6 +32,9 @@ objc_library(
|
||||
"SNTNotificationManager.m",
|
||||
"main.m",
|
||||
],
|
||||
hdrs = [
|
||||
"SNTNotificationManager.h",
|
||||
],
|
||||
data = [
|
||||
"Resources/AboutWindow.xib",
|
||||
"Resources/DeviceMessageWindow.xib",
|
||||
@@ -49,6 +53,7 @@ objc_library(
|
||||
"//Source/common:SNTLogging",
|
||||
"//Source/common:SNTStoredEvent",
|
||||
"//Source/common:SNTStrengthify",
|
||||
"//Source/common:SNTSyncConstants",
|
||||
"//Source/common:SNTXPCControlInterface",
|
||||
"//Source/common:SNTXPCNotifierInterface",
|
||||
"@MOLCertificate",
|
||||
@@ -74,14 +79,41 @@ macos_application(
|
||||
"--force",
|
||||
"--options library,kill,runtime",
|
||||
],
|
||||
entitlements = "Santa.app.entitlements",
|
||||
entitlements = select({
|
||||
"//:adhoc_build": "Santa.app-adhoc.entitlements",
|
||||
# Non-adhoc builds get thier entitlements from the provisioning profile.
|
||||
"//conditions:default": None,
|
||||
}),
|
||||
infoplists = ["Info.plist"],
|
||||
minimum_os_version = "10.15",
|
||||
provisioning_profile = select({
|
||||
"//:ci_build": None,
|
||||
"//:adhoc_build": None,
|
||||
"//conditions:default": "//profiles:santa_dev",
|
||||
}),
|
||||
version = "//:version",
|
||||
visibility = ["//:santa_package_group"],
|
||||
deps = [":SantaGUI_lib"],
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
name = "SNTNotificationManagerTest",
|
||||
srcs = [
|
||||
"SNTNotificationManagerTest.m",
|
||||
],
|
||||
sdk_frameworks = [
|
||||
"Cocoa",
|
||||
],
|
||||
deps = [
|
||||
":SantaGUI_lib",
|
||||
"//Source/common:SNTStoredEvent",
|
||||
"@OCMock",
|
||||
],
|
||||
)
|
||||
|
||||
test_suite(
|
||||
name = "unit_tests",
|
||||
tests = [
|
||||
":SNTNotificationManagerTest",
|
||||
],
|
||||
visibility = ["//:santa_package_group"],
|
||||
)
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/santa/SNTAboutWindowController.h"
|
||||
#import "Source/gui/SNTAboutWindowController.h"
|
||||
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/santa/SNTAccessibleTextField.h"
|
||||
#import "Source/gui/SNTAccessibleTextField.h"
|
||||
|
||||
@implementation SNTAccessibleTextField
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/santa/SNTAppDelegate.h"
|
||||
#import "Source/gui/SNTAppDelegate.h"
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStrengthify.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santa/SNTAboutWindowController.h"
|
||||
#import "Source/santa/SNTNotificationManager.h"
|
||||
#import "Source/gui/SNTAboutWindowController.h"
|
||||
#import "Source/gui/SNTNotificationManager.h"
|
||||
|
||||
@interface SNTAppDelegate ()
|
||||
@property SNTAboutWindowController *aboutWindowController;
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "Source/santa/SNTMessageWindowController.h"
|
||||
#import "Source/gui/SNTMessageWindowController.h"
|
||||
|
||||
@class SNTStoredEvent;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/santa/SNTBinaryMessageWindowController.h"
|
||||
#import "Source/gui/SNTBinaryMessageWindowController.h"
|
||||
|
||||
#import <MOLCertificate/MOLCertificate.h>
|
||||
#import <SecurityInterface/SFCertificatePanel.h>
|
||||
@@ -20,7 +20,7 @@
|
||||
#import "Source/common/SNTBlockMessage.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#import "Source/santa/SNTMessageWindow.h"
|
||||
#import "Source/gui/SNTMessageWindow.h"
|
||||
|
||||
@interface SNTBinaryMessageWindowController ()
|
||||
/// The custom message to display for this event
|
||||
@@ -139,7 +139,9 @@
|
||||
- (NSString *)publisherInfo {
|
||||
MOLCertificate *leafCert = [self.event.signingChain firstObject];
|
||||
|
||||
if (leafCert.commonName && leafCert.orgName) {
|
||||
if ([leafCert.commonName isEqualToString:@"Apple Mac OS Application Signing"]) {
|
||||
return [NSString stringWithFormat:@"App Store (Team ID: %@)", self.event.teamID];
|
||||
} else if (leafCert.commonName && leafCert.orgName) {
|
||||
return [NSString stringWithFormat:@"%@ - %@", leafCert.orgName, leafCert.commonName];
|
||||
} else if (leafCert.commonName) {
|
||||
return leafCert.commonName;
|
||||
@@ -14,7 +14,7 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "Source/common/SNTDeviceEvent.h"
|
||||
#import "Source/santa/SNTMessageWindowController.h"
|
||||
#import "Source/gui/SNTMessageWindowController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/santa/SNTDeviceMessageWindowController.h"
|
||||
#import "Source/gui/SNTDeviceMessageWindowController.h"
|
||||
|
||||
#import "Source/common/SNTBlockMessage.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTDeviceEvent.h"
|
||||
#import "Source/santa/SNTMessageWindow.h"
|
||||
#import "Source/gui/SNTMessageWindow.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/santa/SNTMessageWindow.h"
|
||||
#import "Source/gui/SNTMessageWindow.h"
|
||||
|
||||
@implementation SNTMessageWindow
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#import "Source/santa/SNTMessageWindowController.h"
|
||||
#import "Source/gui/SNTMessageWindowController.h"
|
||||
|
||||
#import "Source/santa/SNTMessageWindow.h"
|
||||
#import "Source/gui/SNTMessageWindow.h"
|
||||
|
||||
@implementation SNTMessageWindowController
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "Source/common/SNTXPCNotifierInterface.h"
|
||||
#import "Source/santa/SNTBinaryMessageWindowController.h"
|
||||
#import "Source/santa/SNTDeviceMessageWindowController.h"
|
||||
#import "Source/santa/SNTMessageWindowController.h"
|
||||
#import "Source/gui/SNTBinaryMessageWindowController.h"
|
||||
#import "Source/gui/SNTDeviceMessageWindowController.h"
|
||||
#import "Source/gui/SNTMessageWindowController.h"
|
||||
|
||||
///
|
||||
/// Keeps track of pending notifications and ensures only one is presented to the user at a time.
|
||||
@@ -12,8 +12,9 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/santa/SNTNotificationManager.h"
|
||||
#import "Source/gui/SNTNotificationManager.h"
|
||||
|
||||
#import <MOLCertificate/MOLCertificate.h>
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
|
||||
@@ -23,8 +24,9 @@
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#import "Source/common/SNTStrengthify.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santa/SNTMessageWindowController.h"
|
||||
#import "Source/gui/SNTMessageWindowController.h"
|
||||
|
||||
@interface SNTNotificationManager ()
|
||||
|
||||
@@ -112,12 +114,59 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
|
||||
|
||||
pendingMsg.delegate = self;
|
||||
[self.pendingNotifications addObject:pendingMsg];
|
||||
[self postDistributedNotification:pendingMsg];
|
||||
|
||||
if (!self.currentWindowController) {
|
||||
[self showQueuedWindow];
|
||||
}
|
||||
}
|
||||
|
||||
// For blocked execution notifications, post an NSDistributedNotificationCenter
|
||||
// notification with the important details from the stored event. Distributed
|
||||
// notifications are system-wide broadcasts that can be sent by apps and observed
|
||||
// from separate processes. This allows users of Santa to write tools that
|
||||
// perform actions when we block execution, such as trigger management tools or
|
||||
// display an enterprise-specific UI (which is particularly useful when combined
|
||||
// with the EnableSilentMode configuration option, to disable Santa's standard UI).
|
||||
- (void)postDistributedNotification:(SNTMessageWindowController *)pendingMsg {
|
||||
if (![pendingMsg isKindOfClass:[SNTBinaryMessageWindowController class]]) {
|
||||
return;
|
||||
}
|
||||
SNTBinaryMessageWindowController *wc = (SNTBinaryMessageWindowController *)pendingMsg;
|
||||
NSDistributedNotificationCenter *dc = [NSDistributedNotificationCenter defaultCenter];
|
||||
NSMutableArray<NSDictionary *> *signingChain =
|
||||
[NSMutableArray arrayWithCapacity:wc.event.signingChain.count];
|
||||
for (MOLCertificate *cert in wc.event.signingChain) {
|
||||
[signingChain addObject:@{
|
||||
kCertSHA256 : cert.SHA256 ?: @"",
|
||||
kCertCN : cert.commonName ?: @"",
|
||||
kCertOrg : cert.orgName ?: @"",
|
||||
kCertOU : cert.orgUnit ?: @"",
|
||||
kCertValidFrom : @([cert.validFrom timeIntervalSince1970]) ?: @0,
|
||||
kCertValidUntil : @([cert.validUntil timeIntervalSince1970]) ?: @0,
|
||||
}];
|
||||
}
|
||||
NSDictionary *userInfo = @{
|
||||
kFileSHA256 : wc.event.fileSHA256 ?: @"",
|
||||
kFilePath : wc.event.filePath ?: @"",
|
||||
kFileBundleName : wc.event.fileBundleName ?: @"",
|
||||
kFileBundleID : wc.event.fileBundleID ?: @"",
|
||||
kFileBundleVersion : wc.event.fileBundleVersion ?: @"",
|
||||
kFileBundleShortVersionString : wc.event.fileBundleVersionString ?: @"",
|
||||
kTeamID : wc.event.teamID ?: @"",
|
||||
kExecutingUser : wc.event.executingUser ?: @"",
|
||||
kExecutionTime : @([wc.event.occurrenceDate timeIntervalSince1970]) ?: @0,
|
||||
kPID : wc.event.pid ?: @0,
|
||||
kPPID : wc.event.ppid ?: @0,
|
||||
kParentName : wc.event.parentName ?: @"",
|
||||
kSigningChain : signingChain,
|
||||
};
|
||||
|
||||
[dc postNotificationName:@"com.google.santa.notification.blockedeexecution"
|
||||
object:@"com.google.santa"
|
||||
userInfo:userInfo];
|
||||
}
|
||||
|
||||
- (void)showQueuedWindow {
|
||||
// Notifications arrive on a background thread but UI updates must happen on the main thread.
|
||||
// This includes making windows.
|
||||
@@ -208,6 +257,8 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
|
||||
#pragma mark SNTNotifierXPC protocol methods
|
||||
|
||||
- (void)postClientModeNotification:(SNTClientMode)clientmode {
|
||||
if ([SNTConfigurator configurator].enableSilentMode) return;
|
||||
|
||||
UNUserNotificationCenter *un = [UNUserNotificationCenter currentNotificationCenter];
|
||||
|
||||
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
|
||||
@@ -246,6 +297,8 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
|
||||
}
|
||||
|
||||
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message {
|
||||
if ([SNTConfigurator configurator].enableSilentMode) return;
|
||||
|
||||
UNUserNotificationCenter *un = [UNUserNotificationCenter currentNotificationCenter];
|
||||
|
||||
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
|
||||
@@ -262,6 +315,8 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
|
||||
}
|
||||
|
||||
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message {
|
||||
if ([SNTConfigurator configurator].enableSilentMode) return;
|
||||
|
||||
if (!event) {
|
||||
LOGI(@"Error: Missing event object in message received from daemon!");
|
||||
return;
|
||||
@@ -274,6 +329,8 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
|
||||
}
|
||||
|
||||
- (void)postUSBBlockNotification:(SNTDeviceEvent *)event withCustomMessage:(NSString *)message {
|
||||
if ([SNTConfigurator configurator].enableSilentMode) return;
|
||||
|
||||
if (!event) {
|
||||
LOGI(@"Error: Missing event object in message received from daemon!");
|
||||
return;
|
||||
76
Source/gui/SNTNotificationManagerTest.m
Normal file
@@ -0,0 +1,76 @@
|
||||
/// 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.
|
||||
|
||||
// #import <MOLCertificate/MOLCertificate.h>
|
||||
// #import <MOLCodesignChecker/MOLCodesignChecker.h>
|
||||
#import <OCMock/OCMock.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "Source/gui/SNTNotificationManager.h"
|
||||
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
|
||||
@class SNTBinaryMessageWindowController;
|
||||
|
||||
@interface SNTNotificationManager (Testing)
|
||||
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event
|
||||
withController:(SNTBinaryMessageWindowController *)controller;
|
||||
@end
|
||||
|
||||
@interface SNTNotificationManagerTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation SNTNotificationManagerTest
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
fclose(stdout);
|
||||
}
|
||||
|
||||
- (void)testPostBlockNotificationSendsDistributedNotification {
|
||||
SNTStoredEvent *ev = [[SNTStoredEvent alloc] init];
|
||||
ev.fileSHA256 = @"the-sha256";
|
||||
ev.filePath = @"/Applications/Safari.app/Contents/MacOS/Safari";
|
||||
ev.fileBundleName = @"Safari";
|
||||
ev.fileBundlePath = @"/Applications/Safari.app";
|
||||
ev.fileBundleID = @"com.apple.Safari";
|
||||
ev.fileBundleVersion = @"18614.1.14.1.15";
|
||||
ev.fileBundleVersionString = @"16.0";
|
||||
ev.executingUser = @"rah";
|
||||
ev.occurrenceDate = [NSDate dateWithTimeIntervalSince1970:1660221048];
|
||||
ev.decision = SNTEventStateBlockBinary;
|
||||
ev.pid = @84156;
|
||||
ev.ppid = @1;
|
||||
ev.parentName = @"launchd";
|
||||
|
||||
SNTNotificationManager *sut = OCMPartialMock([[SNTNotificationManager alloc] init]);
|
||||
OCMStub([sut hashBundleBinariesForEvent:OCMOCK_ANY withController:OCMOCK_ANY]).andDo(nil);
|
||||
|
||||
id dncMock = OCMClassMock([NSDistributedNotificationCenter class]);
|
||||
OCMStub([dncMock defaultCenter]).andReturn(dncMock);
|
||||
|
||||
[sut postBlockNotification:ev withCustomMessage:@""];
|
||||
|
||||
OCMVerify([dncMock postNotificationName:@"com.google.santa.notification.blockedeexecution"
|
||||
object:@"com.google.santa"
|
||||
userInfo:[OCMArg checkWithBlock:^BOOL(NSDictionary *userInfo) {
|
||||
XCTAssertEqualObjects(userInfo[@"file_sha256"], @"the-sha256");
|
||||
XCTAssertEqualObjects(userInfo[@"pid"], @84156);
|
||||
XCTAssertEqualObjects(userInfo[@"ppid"], @1);
|
||||
XCTAssertEqualObjects(userInfo[@"execution_time"], @1660221048);
|
||||
return YES;
|
||||
}]]);
|
||||
}
|
||||
|
||||
@end
|
||||
8
Source/gui/Santa.app-adhoc.entitlements
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.system-extension.install</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santa/SNTAppDelegate.h"
|
||||
#import "Source/gui/SNTAppDelegate.h"
|
||||
|
||||
@interface SNTSystemExtensionDelegate : NSObject <OSSystemExtensionRequestDelegate>
|
||||
@end
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.application-identifier</key>
|
||||
<string>EQHXZ8M8AV.com.google.santa</string>
|
||||
<key>com.apple.developer.system-extension.install</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.team-identifier</key>
|
||||
<string>EQHXZ8M8AV</string>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>EQHXZ8M8AV.com.google.santa</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -36,7 +36,7 @@ macos_command_line_application(
|
||||
infoplists = ["Info.plist"],
|
||||
minimum_os_version = "10.15",
|
||||
provisioning_profile = select({
|
||||
"//:ci_build": None,
|
||||
"//:adhoc_build": None,
|
||||
"//conditions:default": "//profiles:santa_dev",
|
||||
}),
|
||||
version = "//:version",
|
||||
|
||||
@@ -69,7 +69,7 @@ macos_command_line_application(
|
||||
infoplists = ["Info.plist"],
|
||||
minimum_os_version = "10.15",
|
||||
provisioning_profile = select({
|
||||
"//:ci_build": None,
|
||||
"//:adhoc_build": None,
|
||||
"//conditions:default": "//profiles:santa_dev",
|
||||
}),
|
||||
version = "//:version",
|
||||
@@ -102,10 +102,10 @@ santa_unit_test(
|
||||
santa_unit_test(
|
||||
name = "SNTCommandMetricsTest",
|
||||
srcs = [
|
||||
"SNTCommand.h",
|
||||
"SNTCommandController.h",
|
||||
"Commands/SNTCommandMetrics.h",
|
||||
"Commands/SNTCommandMetricsTest.m",
|
||||
"SNTCommand.h",
|
||||
"SNTCommandController.h",
|
||||
],
|
||||
structured_resources = glob(["Commands/testdata/*"]),
|
||||
visibility = ["//:santa_package_group"],
|
||||
@@ -116,8 +116,8 @@ santa_unit_test(
|
||||
"//Source/common:SNTMetricSet",
|
||||
"//Source/common:SNTXPCControlInterface",
|
||||
"//Source/santametricservice/Formats:SNTMetricFormatTestHelper",
|
||||
"@OCMock",
|
||||
"@MOLXPCConnection",
|
||||
"@OCMock",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ REGISTER_COMMAND_NAME(@"metrics")
|
||||
printf(">>> Root Labels\n");
|
||||
[self prettyPrintRootLabels:normalizedMetrics[@"root_labels"]];
|
||||
printf("\n");
|
||||
printf(">>> Metrics \n");
|
||||
printf(">>> Metrics\n");
|
||||
[self prettyPrintMetricValues:normalizedMetrics[@"metrics"]];
|
||||
}
|
||||
|
||||
|
||||
@@ -74,14 +74,15 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
|
||||
- (void)runWithArguments:(NSArray *)arguments {
|
||||
SNTConfigurator *config = [SNTConfigurator configurator];
|
||||
// DEBUG builds add a --force flag to allow manually adding/removing rules during testing.
|
||||
if ((config.syncBaseURL || config.staticRules.count) &&
|
||||
![arguments containsObject:@"--check"]
|
||||
#ifdef DEBUG
|
||||
if ([config syncBaseURL] && ![arguments containsObject:@"--check"] &&
|
||||
![arguments containsObject:@"--force"]) {
|
||||
// DEBUG builds add a --force flag to allow manually adding/removing rules during testing.
|
||||
&& ![arguments containsObject:@"--force"]) {
|
||||
#else
|
||||
if ([config syncBaseURL] && ![arguments containsObject:@"--check"]) {
|
||||
) {
|
||||
#endif
|
||||
printf("SyncBaseURL is set, rules are managed centrally.\n");
|
||||
printf("(SyncBaseURL/StaticRules is set, rules are managed centrally.)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,14 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
// Static rule count
|
||||
__block int64_t staticRuleCount = -1;
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] staticRuleCount:^(int64_t count) {
|
||||
staticRuleCount = count;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
// Sync status
|
||||
__block NSDate *fullSyncLastSuccess;
|
||||
dispatch_group_enter(group);
|
||||
@@ -166,11 +174,11 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
NSString *ruleSyncLastSuccessStr =
|
||||
[dateFormatter stringFromDate:ruleSyncLastSuccess] ?: fullSyncLastSuccessStr;
|
||||
|
||||
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
|
||||
NSString *syncURLStr = configurator.syncBaseURL.absoluteString;
|
||||
|
||||
BOOL exportMetrics = [[SNTConfigurator configurator] exportMetrics];
|
||||
NSURL *metricsURLStr = [[SNTConfigurator configurator] metricURL];
|
||||
NSUInteger metricExportInterval = [[SNTConfigurator configurator] metricExportInterval];
|
||||
BOOL exportMetrics = configurator.exportMetrics;
|
||||
NSURL *metricsURLStr = configurator.metricURL;
|
||||
NSUInteger metricExportInterval = configurator.metricExportInterval;
|
||||
|
||||
if ([arguments containsObject:@"--json"]) {
|
||||
NSMutableDictionary *stats = [@{
|
||||
@@ -194,6 +202,9 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
@"transitive_rules" : @(transitiveRuleCount),
|
||||
@"events_pending_upload" : @(eventCount),
|
||||
},
|
||||
@"static_rules" : @{
|
||||
@"rule_count" : @(staticRuleCount),
|
||||
},
|
||||
@"sync" : @{
|
||||
@"server" : syncURLStr ?: @"null",
|
||||
@"clean_required" : @(syncCleanReqd),
|
||||
@@ -241,6 +252,11 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
printf(" %-25s | %lld\n", "Transitive Rules", transitiveRuleCount);
|
||||
printf(" %-25s | %lld\n", "Events Pending Upload", eventCount);
|
||||
|
||||
if ([SNTConfigurator configurator].staticRules.count) {
|
||||
printf(">>> Static Rules\n");
|
||||
printf(" %-25s | %lld\n", "Rules", staticRuleCount);
|
||||
}
|
||||
|
||||
if (syncURLStr) {
|
||||
printf(">>> Sync Info\n");
|
||||
printf(" %-25s | %s\n", "Sync Server", [syncURLStr UTF8String]);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
hostname | testHost
|
||||
username | testUser
|
||||
|
||||
>>> Metrics
|
||||
>>> Metrics
|
||||
Metric Name | /santa/rules
|
||||
Description | Number of rules
|
||||
Type | SNTMetricTypeGaugeInt64
|
||||
|
||||
@@ -127,8 +127,8 @@ objc_library(
|
||||
"//Source/common:SNTCachedDecision",
|
||||
"//Source/common:SNTCommon",
|
||||
"//Source/common:SNTConfigurator",
|
||||
"//Source/common:SNTStoredEvent",
|
||||
"//Source/common:SNTLogging",
|
||||
"//Source/common:SNTStoredEvent",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -150,13 +150,13 @@ objc_library(
|
||||
|
||||
objc_library(
|
||||
name = "event_logs",
|
||||
hdrs = ["Logs/SNTEventLog.h"],
|
||||
deps = [
|
||||
":file_event_logs",
|
||||
":protobuf_event_logs",
|
||||
":syslog_event_logs",
|
||||
"//Source/common:SNTCommon",
|
||||
],
|
||||
hdrs = ["Logs/SNTEventLog.h"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
@@ -197,11 +197,10 @@ objc_library(
|
||||
"IOKit",
|
||||
],
|
||||
deps = [
|
||||
":SNTApplicationCoreMetrics",
|
||||
":database_controller",
|
||||
":endpoint_security_manager",
|
||||
":event_logs",
|
||||
":SNTApplicationCoreMetrics",
|
||||
"//Source/common:SantaCache",
|
||||
"//Source/common:SNTAllowlistInfo",
|
||||
"//Source/common:SNTBlockMessage",
|
||||
"//Source/common:SNTCachedDecision",
|
||||
@@ -222,6 +221,7 @@ objc_library(
|
||||
"//Source/common:SNTXPCNotifierInterface",
|
||||
"//Source/common:SNTXPCSyncServiceInterface",
|
||||
"//Source/common:SNTXPCUnprivilegedControlInterface",
|
||||
"//Source/common:SantaCache",
|
||||
"@FMDB",
|
||||
"@MOLCertificate",
|
||||
"@MOLCodesignChecker",
|
||||
@@ -287,11 +287,16 @@ macos_bundle(
|
||||
"--force",
|
||||
"--options library,kill,runtime",
|
||||
],
|
||||
entitlements = select({
|
||||
"//:adhoc_build": "com.google.santa.daemon.systemextension-adhoc.entitlements",
|
||||
# Non-adhoc builds get their entitlements from the provisioning profile.
|
||||
"//conditions:default": None,
|
||||
}),
|
||||
infoplists = ["Info.plist"],
|
||||
linkopts = ["-execute"],
|
||||
minimum_os_version = "10.15",
|
||||
provisioning_profile = select({
|
||||
"//:ci_build": None,
|
||||
"//:adhoc_build": None,
|
||||
"//conditions:default": "//profiles:daemon_dev",
|
||||
}),
|
||||
version = "//:version",
|
||||
@@ -302,11 +307,11 @@ macos_bundle(
|
||||
santa_unit_test(
|
||||
name = "SNTExecutionControllerTest",
|
||||
srcs = [
|
||||
"SNTExecutionController.h",
|
||||
"DataLayer/SNTDatabaseTable.h",
|
||||
"DataLayer/SNTEventTable.h",
|
||||
"DataLayer/SNTRuleTable.h",
|
||||
"EventProviders/SNTEventProvider.h",
|
||||
"SNTExecutionController.h",
|
||||
"SNTExecutionControllerTest.m",
|
||||
],
|
||||
sdk_dylibs = [
|
||||
@@ -364,6 +369,9 @@ santa_unit_test(
|
||||
"DataLayer/SNTRuleTable.m",
|
||||
"DataLayer/SNTRuleTableTest.m",
|
||||
],
|
||||
sdk_dylibs = [
|
||||
"EndpointSecurity",
|
||||
],
|
||||
deps = [
|
||||
"//Source/common:SNTCachedDecision",
|
||||
"//Source/common:SNTCommonEnums",
|
||||
@@ -375,9 +383,6 @@ santa_unit_test(
|
||||
"@MOLCertificate",
|
||||
"@MOLCodesignChecker",
|
||||
],
|
||||
sdk_dylibs = [
|
||||
"EndpointSecurity",
|
||||
],
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
@@ -395,8 +400,8 @@ santa_unit_test(
|
||||
],
|
||||
deps = [
|
||||
":EndpointSecurityTestLib",
|
||||
"//Source/common:SNTConfigurator",
|
||||
"//Source/common:SNTCommon",
|
||||
"//Source/common:SNTConfigurator",
|
||||
"//Source/common:SNTLogging",
|
||||
"//Source/common:SNTPrefixTree",
|
||||
"//Source/common:SantaCache",
|
||||
@@ -443,6 +448,7 @@ santa_unit_test(
|
||||
"EndpointSecurity",
|
||||
"bsm",
|
||||
],
|
||||
tags = ["exclusive"],
|
||||
deps = [
|
||||
":EndpointSecurityTestLib",
|
||||
":santad_lib",
|
||||
@@ -453,7 +459,6 @@ santa_unit_test(
|
||||
"@MOLXPCConnection",
|
||||
"@OCMock",
|
||||
],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
|
||||
@@ -277,6 +277,19 @@ static void addPathsFromDefaultMuteSet(NSMutableSet *criticalPaths) API_AVAILABL
|
||||
teamID:(NSString *)teamID {
|
||||
__block SNTRule *rule;
|
||||
|
||||
// Look for a static rule that matches.
|
||||
NSDictionary *staticRules = [[SNTConfigurator configurator] staticRules];
|
||||
if (staticRules.count) {
|
||||
rule = staticRules[binarySHA256];
|
||||
if (rule.type == SNTRuleTypeBinary) return rule;
|
||||
rule = staticRules[certificateSHA256];
|
||||
if (rule.type == SNTRuleTypeCertificate) return rule;
|
||||
rule = staticRules[teamID];
|
||||
if (rule.type == SNTRuleTypeTeamID) return rule;
|
||||
}
|
||||
|
||||
// Now query the database.
|
||||
//
|
||||
// NOTE: This code is written with the intention that the binary rule is searched for first
|
||||
// as Santa is designed to go with the most-specific rule possible.
|
||||
//
|
||||
|
||||
@@ -313,21 +313,21 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
dispatch_semaphore_signal(processingSema);
|
||||
dispatch_semaphore_t deadlineExpiredSema = dispatch_semaphore_create(0);
|
||||
|
||||
if (m->action_type == ES_ACTION_TYPE_AUTH) {
|
||||
if (mc->action_type == ES_ACTION_TYPE_AUTH) {
|
||||
dispatch_after(timeout, self.esAuthQueue, ^(void) {
|
||||
if (dispatch_semaphore_wait(processingSema, DISPATCH_TIME_NOW) != 0) {
|
||||
// Handler already responded, nothing to do.
|
||||
return;
|
||||
}
|
||||
LOGE(@"SNTDeviceManager: deadline reached: deny pid=%d ret=%d",
|
||||
audit_token_to_pid(m->process->audit_token),
|
||||
es_respond_auth_result(c, m, ES_AUTH_RESULT_DENY, false));
|
||||
audit_token_to_pid(mc->process->audit_token),
|
||||
es_respond_auth_result(c, mc, ES_AUTH_RESULT_DENY, false));
|
||||
dispatch_semaphore_signal(deadlineExpiredSema);
|
||||
});
|
||||
}
|
||||
|
||||
dispatch_async(self.esAuthQueue, ^{
|
||||
[self handleESMessage:m withClient:c];
|
||||
[self handleESMessage:mc withClient:c];
|
||||
if (dispatch_semaphore_wait(processingSema, DISPATCH_TIME_NOW) != 0) {
|
||||
// Deadline expired, wait for deadline block to finish.
|
||||
dispatch_semaphore_wait(deadlineExpiredSema, DISPATCH_TIME_FOREVER);
|
||||
|
||||
@@ -226,6 +226,7 @@
|
||||
exec.argsArray = [(__bridge NSArray *)message.args_array mutableCopy];
|
||||
exec.machineId =
|
||||
[[SNTConfigurator configurator] enableMachineIDDecoration] ? self.machineID : nil;
|
||||
exec.teamId = cd.teamID;
|
||||
|
||||
[self logWithSantaMessage:&message
|
||||
wrapper:^(SNTPBSantaMessage *sm) {
|
||||
|
||||
@@ -170,11 +170,15 @@
|
||||
|
||||
[outLog appendFormat:@"|sha256=%@", cd.sha256];
|
||||
|
||||
if (cd.certSHA256) {
|
||||
if (cd.certSHA256.length) {
|
||||
[outLog appendFormat:@"|cert_sha256=%@|cert_cn=%@", cd.certSHA256,
|
||||
[self sanitizeString:cd.certCommonName]];
|
||||
}
|
||||
|
||||
if (cd.teamID.length) {
|
||||
[outLog appendFormat:@"|teamid=%@", cd.teamID];
|
||||
}
|
||||
|
||||
if (cd.quarantineURL) {
|
||||
[outLog appendFormat:@"|quarantine_url=%@", [self sanitizeString:cd.quarantineURL]];
|
||||
}
|
||||
|
||||
@@ -318,9 +318,14 @@ dispatch_source_t createDispatchTimer(uint64_t interval, uint64_t leeway, dispat
|
||||
// This will handle retying connection establishment if there are issues with the service
|
||||
// during initialization (missing binary, malformed plist, bad code signature, etc.).
|
||||
// Once those issues are resolved the connection will establish.
|
||||
// This will also handle reestablishment if the service crashes or is killed.
|
||||
// This will also handle re-establishment if the service crashes or is killed.
|
||||
WEAKIFY(self);
|
||||
ss.invalidationHandler = ^(void) {
|
||||
[self establishSyncServiceConnection];
|
||||
STRONGIFY(self);
|
||||
self.syncdQueue.syncConnection.invalidationHandler = nil;
|
||||
[self performSelectorOnMainThread:@selector(establishSyncServiceConnection)
|
||||
withObject:nil
|
||||
waitUntilDone:YES];
|
||||
};
|
||||
[ss resume]; // If there are issues establishing the connection resume will block for 2 seconds.
|
||||
self.syncdQueue.syncConnection = ss;
|
||||
|
||||
@@ -43,7 +43,6 @@ double watchdogCPUPeak = 0;
|
||||
double watchdogRAMPeak = 0;
|
||||
|
||||
@interface SNTDaemonControlController ()
|
||||
@property NSString *_syncXsrfToken;
|
||||
@property SNTPolicyProcessor *policyProcessor;
|
||||
@property id<SNTEventProvider> eventProvider;
|
||||
@property SNTNotificationQueue *notQueue;
|
||||
@@ -140,6 +139,10 @@ double watchdogRAMPeak = 0;
|
||||
teamID:teamID]);
|
||||
}
|
||||
|
||||
- (void)staticRuleCount:(void (^)(int64_t count))reply {
|
||||
reply([SNTConfigurator configurator].staticRules.count);
|
||||
}
|
||||
|
||||
#pragma mark Decision Ops
|
||||
|
||||
- (void)decisionForFilePath:(NSString *)filePath
|
||||
@@ -169,15 +172,6 @@ double watchdogRAMPeak = 0;
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)xsrfToken:(void (^)(NSString *))reply {
|
||||
reply(self._syncXsrfToken);
|
||||
}
|
||||
|
||||
- (void)setXsrfToken:(NSString *)token reply:(void (^)(void))reply {
|
||||
self._syncXsrfToken = token;
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply {
|
||||
reply([[SNTConfigurator configurator] fullSyncLastSuccess]);
|
||||
}
|
||||
@@ -257,6 +251,15 @@ double watchdogRAMPeak = 0;
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)disableUnknownEventUpload:(void (^)(BOOL))reply {
|
||||
reply([SNTConfigurator configurator].disableUnknownEventUpload);
|
||||
}
|
||||
|
||||
- (void)setDisableUnknownEventUpload:(BOOL)enabled reply:(void (^)(void))reply {
|
||||
[[SNTConfigurator configurator] setDisableUnknownEventUpload:enabled];
|
||||
reply();
|
||||
}
|
||||
|
||||
#pragma mark Metrics Ops
|
||||
|
||||
- (void)metrics:(void (^)(NSDictionary *))reply {
|
||||
|
||||
@@ -195,11 +195,9 @@ static NSString *const kPrinterProxyPostMonterey =
|
||||
[self incrementEventCounters:cd.decision];
|
||||
|
||||
// Log to database if necessary.
|
||||
if ([SNTConfigurator configurator].enableAllEventUpload ||
|
||||
(cd.decision != SNTEventStateAllowBinary && cd.decision != SNTEventStateAllowCompiler &&
|
||||
cd.decision != SNTEventStateAllowTransitive &&
|
||||
cd.decision != SNTEventStateAllowCertificate && cd.decision != SNTEventStateAllowTeamID &&
|
||||
cd.decision != SNTEventStateAllowScope)) {
|
||||
if (config.enableAllEventUpload ||
|
||||
(cd.decision == SNTEventStateAllowUnknown && !config.disableUnknownEventUpload) ||
|
||||
(cd.decision & SNTEventStateAllow) == 0) {
|
||||
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
|
||||
se.occurrenceDate = [[NSDate alloc] init];
|
||||
se.fileSHA256 = cd.sha256;
|
||||
@@ -207,6 +205,7 @@ static NSString *const kPrinterProxyPostMonterey =
|
||||
se.decision = cd.decision;
|
||||
|
||||
se.signingChain = cd.certChain;
|
||||
se.teamID = cd.teamID;
|
||||
se.pid = @(message.pid);
|
||||
se.ppid = @(message.ppid);
|
||||
se.parentName = @(message.pname);
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
|
||||
fclose(stdout);
|
||||
|
||||
[[SNTMetricSet sharedInstance] reset];
|
||||
|
||||
self.mockCodesignChecker = OCMClassMock([MOLCodesignChecker class]);
|
||||
OCMStub([self.mockCodesignChecker alloc]).andReturn(self.mockCodesignChecker);
|
||||
|
||||
@@ -111,7 +113,7 @@
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundField) {
|
||||
if (!foundField && expectedValue.intValue != 0) {
|
||||
XCTFail(@"failed to find %@ field value", expectedFieldValueName);
|
||||
}
|
||||
}
|
||||
@@ -129,7 +131,7 @@
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
|
||||
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
|
||||
[self checkMetricCounters:@"AllowBinary" expected:@2];
|
||||
[self checkMetricCounters:@"AllowBinary" expected:@1];
|
||||
}
|
||||
|
||||
- (void)testBinaryBlockRule {
|
||||
@@ -241,7 +243,7 @@
|
||||
|
||||
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
|
||||
|
||||
[self checkMetricCounters:@"AllowBinary" expected:@2];
|
||||
[self checkMetricCounters:kAllowTransitive expected:@1];
|
||||
}
|
||||
|
||||
- (void)testBinaryAllowTransitiveRuleDisabled {
|
||||
@@ -262,8 +264,8 @@
|
||||
|
||||
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
|
||||
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
|
||||
[self checkMetricCounters:kAllowBinary expected:@2];
|
||||
[self checkMetricCounters:kAllowTransitive expected:@1];
|
||||
[self checkMetricCounters:kAllowBinary expected:@0];
|
||||
[self checkMetricCounters:kAllowTransitive expected:@0];
|
||||
}
|
||||
|
||||
- (void)testDefaultDecision {
|
||||
@@ -281,7 +283,7 @@
|
||||
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
|
||||
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
|
||||
|
||||
[self checkMetricCounters:kBlockUnknown expected:@2];
|
||||
[self checkMetricCounters:kBlockUnknown expected:@1];
|
||||
[self checkMetricCounters:kAllowUnknown expected:@1];
|
||||
}
|
||||
|
||||
@@ -298,7 +300,7 @@
|
||||
OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
|
||||
[self checkMetricCounters:kAllowNoFileInfo expected:@2];
|
||||
[self checkMetricCounters:kAllowNoFileInfo expected:@1];
|
||||
}
|
||||
|
||||
- (void)testUnreadableFailClosedLockdown {
|
||||
@@ -344,7 +346,7 @@
|
||||
OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
|
||||
[self checkMetricCounters:kAllowScope expected:@2];
|
||||
[self checkMetricCounters:kAllowScope expected:@1];
|
||||
}
|
||||
|
||||
- (void)testPageZero {
|
||||
@@ -354,7 +356,39 @@
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
|
||||
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
|
||||
[self checkMetricCounters:kBlockUnknown expected:@3];
|
||||
[self checkMetricCounters:kBlockUnknown expected:@1];
|
||||
}
|
||||
|
||||
- (void)testAllEventUpload {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
|
||||
OCMExpect([self.mockConfigurator enableAllEventUpload]).andReturn(YES);
|
||||
OCMExpect([self.mockEventDatabase addStoredEvent:OCMOCK_ANY]);
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateAllow;
|
||||
rule.type = SNTRuleTypeBinary;
|
||||
OCMStub([self.mockRuleDatabase ruleForBinarySHA256:@"a" certificateSHA256:nil teamID:nil])
|
||||
.andReturn(rule);
|
||||
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
|
||||
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
|
||||
}
|
||||
|
||||
- (void)testDisableUnknownEventUpload {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
|
||||
OCMExpect([self.mockConfigurator clientMode]).andReturn(SNTClientModeMonitor);
|
||||
OCMExpect([self.mockConfigurator enableAllEventUpload]).andReturn(NO);
|
||||
OCMExpect([self.mockConfigurator disableUnknownEventUpload]).andReturn(YES);
|
||||
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
|
||||
OCMVerify(never(), [self.mockEventDatabase addStoredEvent:OCMOCK_ANY]);
|
||||
[self checkMetricCounters:kAllowUnknown expected:@1];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.endpoint-security.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.application-identifier</key>
|
||||
<string>EQHXZ8M8AV.com.google.santa.daemon</string>
|
||||
<key>com.apple.developer.endpoint-security.client</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.team-identifier</key>
|
||||
<string>EQHXZ8M8AV</string>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>EQHXZ8M8AV.com.google.santa.daemon</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -42,8 +42,8 @@ santa_unit_test(
|
||||
"//Source/common:SNTConfigurator",
|
||||
"//Source/common:SNTMetricSet",
|
||||
"//Source/santametricservice/Formats:SNTMetricFormatTestHelper",
|
||||
"@OCMock",
|
||||
"@MOLAuthenticatingURLSession",
|
||||
"@OCMock",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -67,7 +67,7 @@ macos_command_line_application(
|
||||
infoplists = ["Info.plist"],
|
||||
minimum_os_version = "10.15",
|
||||
provisioning_profile = select({
|
||||
"//:ci_build": None,
|
||||
"//:adhoc_build": None,
|
||||
"//conditions:default": "//profiles:santa_dev",
|
||||
}),
|
||||
version = "//:version",
|
||||
|
||||
@@ -76,6 +76,7 @@ santa_unit_test(
|
||||
deps = [
|
||||
":SNTMetricFormatTestHelper",
|
||||
":SNTMetricMonarchJSONFormat",
|
||||
"@OCMock",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -100,7 +100,8 @@ const NSString *kKey = @"key";
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSDictionary *> *)encodeDataForMetric:(NSDictionary *)metric {
|
||||
- (NSArray<NSDictionary *> *)encodeDataForMetric:(NSDictionary *)metric
|
||||
withEndTimestamp:(NSDate *)endTimestamp {
|
||||
NSMutableArray<NSDictionary *> *monarchMetricData = [[NSMutableArray alloc] init];
|
||||
|
||||
for (NSString *fieldName in metric[@"fields"]) {
|
||||
@@ -112,8 +113,8 @@ const NSString *kKey = @"key";
|
||||
}
|
||||
|
||||
monarchDataEntry[kStartTimestamp] = [self->_dateFormatter stringFromDate:entry[@"created"]];
|
||||
monarchDataEntry[kEndTimestamp] =
|
||||
[self->_dateFormatter stringFromDate:entry[@"last_updated"]];
|
||||
// Monarch wants all the end timestamp to be updated, even if the value does not change.
|
||||
monarchDataEntry[kEndTimestamp] = [self->_dateFormatter stringFromDate:endTimestamp];
|
||||
|
||||
if (!metric[@"type"]) {
|
||||
LOGE(@"metric type is nil");
|
||||
@@ -157,10 +158,13 @@ const NSString *kKey = @"key";
|
||||
}
|
||||
|
||||
/**
|
||||
* formatMetric translates the SNTMetricSet metric entries into those consumable by Monarch.
|
||||
* formatMetric translates the SNTMetricSet metric entries into those consumable
|
||||
* by Monarch.
|
||||
**/
|
||||
|
||||
- (NSDictionary *)formatMetric:(NSString *)name withMetric:(NSDictionary *)metric {
|
||||
- (NSDictionary *)formatMetric:(NSString *)name
|
||||
withValue:(NSDictionary *)metric
|
||||
andEndtimestamp:(NSDate *)endTimestamp {
|
||||
NSMutableDictionary *monarchMetric = [[NSMutableDictionary alloc] init];
|
||||
|
||||
monarchMetric[kMetricName] = name;
|
||||
@@ -175,7 +179,7 @@ const NSString *kKey = @"key";
|
||||
}
|
||||
|
||||
[self encodeValueAndStreamKindFor:name withMetric:metric into:monarchMetric];
|
||||
monarchMetric[@"data"] = [self encodeDataForMetric:metric];
|
||||
monarchMetric[@"data"] = [self encodeDataForMetric:metric withEndTimestamp:endTimestamp];
|
||||
|
||||
return monarchMetric;
|
||||
}
|
||||
@@ -185,10 +189,12 @@ const NSString *kKey = @"key";
|
||||
**/
|
||||
- (NSDictionary *)normalize:(NSDictionary *)metrics {
|
||||
NSMutableArray<NSDictionary *> *monarchMetrics = [[NSMutableArray alloc] init];
|
||||
NSDate *endTimestamp = [NSDate date];
|
||||
|
||||
for (NSString *metricName in metrics[@"metrics"]) {
|
||||
[monarchMetrics addObject:[self formatMetric:metricName
|
||||
withMetric:metrics[@"metrics"][metricName]]];
|
||||
withValue:metrics[@"metrics"][metricName]
|
||||
andEndtimestamp:endTimestamp]];
|
||||
}
|
||||
|
||||
NSMutableArray<NSDictionary *> *rootLabels = [[NSMutableArray alloc] init];
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <OCMock/OCMock.h>
|
||||
#import "Source/santametricservice/Formats/SNTMetricFormatTestHelper.h"
|
||||
#import "Source/santametricservice/Formats/SNTMetricMonarchJSONFormat.h"
|
||||
|
||||
@@ -9,6 +11,10 @@
|
||||
@implementation SNTMetricMonarchJSONFormatTest
|
||||
|
||||
- (void)testMetricsConversionToJSON {
|
||||
id classMock = OCMClassMock([NSDate class]);
|
||||
OCMStub([classMock date])
|
||||
.andReturn([NSDate dateWithTimeIntervalSince1970:1631826490]); // 2021-09-16 21:08:10Z
|
||||
|
||||
NSDictionary *validMetricsDict = [SNTMetricFormatTestHelper createValidMetricsDictionary];
|
||||
SNTMetricMonarchJSONFormat *formatter = [[SNTMetricMonarchJSONFormat alloc] init];
|
||||
NSError *err = nil;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"data" : [
|
||||
{
|
||||
"int64Value" : 1,
|
||||
"endTimestamp" : "2021-09-16T21:07:34.826Z",
|
||||
"endTimestamp" : "2021-09-16T21:08:10.000Z",
|
||||
"field" : [
|
||||
{
|
||||
"name" : "rule_type",
|
||||
@@ -37,7 +37,7 @@
|
||||
},
|
||||
{
|
||||
"int64Value" : 3,
|
||||
"endTimestamp" : "2021-09-16T21:07:34.826Z",
|
||||
"endTimestamp" : "2021-09-16T21:08:10.000Z",
|
||||
"field" : [
|
||||
{
|
||||
"name" : "rule_type",
|
||||
@@ -54,7 +54,7 @@
|
||||
"data" : [
|
||||
{
|
||||
"int64Value" : 123456789,
|
||||
"endTimestamp" : "2021-09-16T21:07:34.826Z",
|
||||
"endTimestamp" : "2021-09-16T21:08:10.000Z",
|
||||
"startTimestamp" : "2021-09-16T21:07:34.826Z"
|
||||
}
|
||||
],
|
||||
@@ -73,7 +73,7 @@
|
||||
"data" : [
|
||||
{
|
||||
"int64Value" : 1,
|
||||
"endTimestamp" : "2021-09-16T21:07:34.826Z",
|
||||
"endTimestamp" : "2021-09-16T21:08:10.000Z",
|
||||
"field" : [
|
||||
{
|
||||
"name" : "rule_type",
|
||||
@@ -84,7 +84,7 @@
|
||||
},
|
||||
{
|
||||
"int64Value" : 2,
|
||||
"endTimestamp" : "2021-09-16T21:07:34.826Z",
|
||||
"endTimestamp" : "2021-09-16T21:08:10.000Z",
|
||||
"field" : [
|
||||
{
|
||||
"name" : "rule_type",
|
||||
@@ -104,7 +104,7 @@
|
||||
"data" : [
|
||||
{
|
||||
"boolValue" : true,
|
||||
"endTimestamp" : "2021-09-16T21:07:34.826Z",
|
||||
"endTimestamp" : "2021-09-16T21:08:10.000Z",
|
||||
"startTimestamp" : "2021-09-16T21:07:34.826Z"
|
||||
}
|
||||
]
|
||||
@@ -116,7 +116,7 @@
|
||||
"data" : [
|
||||
{
|
||||
"int64Value" : 1250999830800,
|
||||
"endTimestamp" : "2021-09-16T21:07:34.826Z",
|
||||
"endTimestamp" : "2021-09-16T21:08:10.000Z",
|
||||
"startTimestamp" : "2021-09-16T21:07:34.826Z"
|
||||
}
|
||||
]
|
||||
@@ -127,7 +127,7 @@
|
||||
"data" : [
|
||||
{
|
||||
"int64Value" : 987654321,
|
||||
"endTimestamp" : "2021-09-16T21:07:34.826Z",
|
||||
"endTimestamp" : "2021-09-16T21:08:10.000Z",
|
||||
"startTimestamp" : "2021-09-16T21:07:34.826Z"
|
||||
}
|
||||
],
|
||||
@@ -141,7 +141,7 @@
|
||||
"data" : [
|
||||
{
|
||||
"stringValue" : "20210809.0.1",
|
||||
"endTimestamp" : "2021-09-16T21:07:34.826Z",
|
||||
"endTimestamp" : "2021-09-16T21:08:10.000Z",
|
||||
"startTimestamp" : "2021-09-16T21:07:34.826Z"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -164,6 +164,10 @@ NSDictionary *validMetricsDict = nil;
|
||||
}
|
||||
|
||||
- (void)testWritingMonarchJSONToAFile {
|
||||
id classMock = OCMClassMock([NSDate class]);
|
||||
OCMStub([classMock date])
|
||||
.andReturn([NSDate dateWithTimeIntervalSince1970:1631826490]); // 2021-09-16 21:08:10Z
|
||||
|
||||
OCMStub([self.mockConfigurator exportMetrics]).andReturn(YES);
|
||||
OCMStub([self.mockConfigurator metricFormat]).andReturn(SNTMetricFormatTypeMonarchJSON);
|
||||
OCMStub([self.mockConfigurator metricURL]).andReturn(self.jsonURL);
|
||||
|
||||
@@ -62,8 +62,8 @@ santa_unit_test(
|
||||
deps = [
|
||||
":SNTMetricHTTPWriter",
|
||||
"//Source/common:SNTConfigurator",
|
||||
"@OCMock",
|
||||
"@MOLAuthenticatingURLSession",
|
||||
"@OCMock",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@ objc_library(
|
||||
"SNTPushNotifications.m",
|
||||
"SNTPushNotificationsTracker.h",
|
||||
"SNTPushNotificationsTracker.m",
|
||||
"SNTSyncConstants.h",
|
||||
"SNTSyncConstants.m",
|
||||
"SNTSyncEventUpload.h",
|
||||
"SNTSyncEventUpload.m",
|
||||
"SNTSyncLogging.h",
|
||||
@@ -57,6 +55,7 @@ objc_library(
|
||||
"//Source/common:SNTRule",
|
||||
"//Source/common:SNTStoredEvent",
|
||||
"//Source/common:SNTStrengthify",
|
||||
"//Source/common:SNTSyncConstants",
|
||||
"//Source/common:SNTSystemInfo",
|
||||
"//Source/common:SNTXPCControlInterface",
|
||||
"//Source/common:SNTXPCSyncServiceInterface",
|
||||
@@ -78,8 +77,6 @@ santa_unit_test(
|
||||
"SNTPushNotifications.m",
|
||||
"SNTPushNotificationsTracker.h",
|
||||
"SNTPushNotificationsTracker.m",
|
||||
"SNTSyncConstants.h",
|
||||
"SNTSyncConstants.m",
|
||||
"SNTSyncEventUpload.h",
|
||||
"SNTSyncEventUpload.m",
|
||||
"SNTSyncLogging.h",
|
||||
@@ -113,6 +110,7 @@ santa_unit_test(
|
||||
"//Source/common:SNTRule",
|
||||
"//Source/common:SNTStoredEvent",
|
||||
"//Source/common:SNTStrengthify",
|
||||
"//Source/common:SNTSyncConstants",
|
||||
"//Source/common:SNTSystemInfo",
|
||||
"//Source/common:SNTXPCControlInterface",
|
||||
"@MOLAuthenticatingURLSession",
|
||||
@@ -122,6 +120,17 @@ santa_unit_test(
|
||||
],
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
name = "NSDataZlibTest",
|
||||
srcs = [
|
||||
"NSData+Zlib.h",
|
||||
"NSData+Zlib.m",
|
||||
"NSDataZlibTest.m",
|
||||
],
|
||||
resources = glob(["testdata/sync_preflight_basic.*"]),
|
||||
sdk_dylibs = ["libz"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "broadcaster_lib",
|
||||
srcs = ["SNTSyncBroadcaster.m"],
|
||||
@@ -162,7 +171,7 @@ macos_command_line_application(
|
||||
infoplists = ["Info.plist"],
|
||||
minimum_os_version = "10.15",
|
||||
provisioning_profile = select({
|
||||
"//:ci_build": None,
|
||||
"//:adhoc_build": None,
|
||||
"//conditions:default": "//profiles:santa_dev",
|
||||
}),
|
||||
version = "//:version",
|
||||
@@ -173,6 +182,7 @@ macos_command_line_application(
|
||||
test_suite(
|
||||
name = "unit_tests",
|
||||
tests = [
|
||||
":NSDataZlibTest",
|
||||
":SNTSyncTest",
|
||||
],
|
||||
visibility = ["//:santa_package_group"],
|
||||
|
||||
54
Source/santasyncservice/NSDataZlibTest.m
Normal file
@@ -0,0 +1,54 @@
|
||||
/// 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.
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "Source/santasyncservice/NSData+Zlib.h"
|
||||
|
||||
@interface NSDataZlibTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation NSDataZlibTest
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
}
|
||||
|
||||
- (NSData *)dataFromFixture:(NSString *)file {
|
||||
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:file ofType:nil];
|
||||
XCTAssertNotNil(path, @"failed to load testdata: %@", file);
|
||||
return [NSData dataWithContentsOfFile:path];
|
||||
}
|
||||
|
||||
- (void)testZlibCompressed {
|
||||
NSData *sut = [self dataFromFixture:@"sync_preflight_basic.json"];
|
||||
NSData *want = [self dataFromFixture:@"sync_preflight_basic.z"];
|
||||
|
||||
XCTAssertEqualObjects([sut zlibCompressed], want);
|
||||
}
|
||||
|
||||
- (void)testGzipCompressed {
|
||||
NSData *sut = [self dataFromFixture:@"sync_preflight_basic.json"];
|
||||
NSData *want = [self dataFromFixture:@"sync_preflight_basic.gz"];
|
||||
|
||||
XCTAssertEqualObjects([sut gzipCompressed], want);
|
||||
}
|
||||
|
||||
- (void)testCompressEmpty {
|
||||
NSData *sut = [NSData data];
|
||||
XCTAssertNil([sut zlibCompressed]);
|
||||
XCTAssertNil([sut gzipCompressed]);
|
||||
};
|
||||
|
||||
@end
|
||||
@@ -17,8 +17,8 @@
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStrengthify.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
#import "Source/santasyncservice/SNTPushNotificationsTracker.h"
|
||||
#import "Source/santasyncservice/SNTSyncConstants.h"
|
||||
#import "Source/santasyncservice/SNTSyncFCM.h"
|
||||
#import "Source/santasyncservice/SNTSyncState.h"
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#import "Source/santasyncservice/SNTPushNotificationsTracker.h"
|
||||
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/santasyncservice/SNTSyncConstants.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
|
||||
@interface SNTPushNotificationsTracker ()
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
#import "Source/common/SNTFileInfo.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santasyncservice/NSData+Zlib.h"
|
||||
#import "Source/santasyncservice/SNTSyncConstants.h"
|
||||
#import "Source/santasyncservice/SNTSyncLogging.h"
|
||||
#import "Source/santasyncservice/SNTSyncState.h"
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
[signingChain addObject:certDict];
|
||||
}
|
||||
newEvent[kSigningChain] = signingChain;
|
||||
ADDKEY(newEvent, kTeamID, event.teamID);
|
||||
|
||||
return newEvent;
|
||||
#undef ADDKEY
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#import "Source/common/SNTStrengthify.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santasyncservice/SNTPushNotifications.h"
|
||||
#import "Source/santasyncservice/SNTSyncConstants.h"
|
||||
#import "Source/santasyncservice/SNTSyncEventUpload.h"
|
||||
#import "Source/santasyncservice/SNTSyncLogging.h"
|
||||
#import "Source/santasyncservice/SNTSyncPostflight.h"
|
||||
@@ -53,6 +53,8 @@ static const uint8_t kMaxEnqueuedSyncs = 2;
|
||||
|
||||
@property NSUInteger eventBatchSize;
|
||||
|
||||
@property NSString *xsrfToken;
|
||||
|
||||
@end
|
||||
|
||||
// Called when the network state changes
|
||||
@@ -120,6 +122,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
|
||||
[self startReachability];
|
||||
}
|
||||
self.xsrfToken = syncState.xsrfToken;
|
||||
}
|
||||
|
||||
- (void)postBundleEventToSyncServer:(SNTStoredEvent *)event
|
||||
@@ -152,6 +155,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
|
||||
[self startReachability];
|
||||
}
|
||||
self.xsrfToken = syncState.xsrfToken;
|
||||
}
|
||||
|
||||
- (void)isFCMListening:(void (^)(BOOL))reply {
|
||||
@@ -222,6 +226,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
SNTSyncRuleDownload *p = [[SNTSyncRuleDownload alloc] initWithState:syncState];
|
||||
BOOL ret = [p sync];
|
||||
LOGD(@"Rule download %@", ret ? @"complete" : @"failed");
|
||||
self.xsrfToken = syncState.xsrfToken;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -246,6 +251,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
SNTSyncPreflight *p = [[SNTSyncPreflight alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
SLOGD(@"Preflight complete");
|
||||
self.xsrfToken = syncState.xsrfToken;
|
||||
|
||||
// Clean up reachability if it was started for a non-network error
|
||||
[self stopReachability];
|
||||
@@ -346,12 +352,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
SLOGW(@"Missing Machine Owner.");
|
||||
}
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] xsrfToken:^(NSString *token) {
|
||||
syncState.xsrfToken = token;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
syncState.xsrfToken = self.xsrfToken;
|
||||
|
||||
NSURLSessionConfiguration *sessConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
sessConfig.connectionProxyDictionary = [[SNTConfigurator configurator] syncProxyConfig];
|
||||
@@ -394,7 +395,6 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
|
||||
syncState.pushNotificationsToken = self.pushNotifications.token;
|
||||
|
||||
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
|
||||
return syncState;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santasyncservice/SNTSyncConstants.h"
|
||||
#import "Source/santasyncservice/SNTSyncState.h"
|
||||
|
||||
@implementation SNTSyncPostflight
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
#import "Source/common/SNTCommon.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
#import "Source/common/SNTSystemInfo.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santasyncservice/SNTSyncConstants.h"
|
||||
#import "Source/santasyncservice/SNTSyncLogging.h"
|
||||
#import "Source/santasyncservice/SNTSyncState.h"
|
||||
|
||||
@@ -117,6 +117,14 @@
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
dispatch_group_enter(group);
|
||||
NSNumber *disableUnknownEventUpload = resp[kDisableUnknownEventUpload];
|
||||
[[self.daemonConn remoteObjectProxy]
|
||||
setDisableUnknownEventUpload:[disableUnknownEventUpload boolValue]
|
||||
reply:^{
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
self.syncState.eventBatchSize = [resp[kBatchSize] unsignedIntegerValue] ?: kDefaultEventBatchSize;
|
||||
|
||||
// Don't let these go too low
|
||||
|
||||
@@ -19,5 +19,4 @@
|
||||
@class SNTRule;
|
||||
|
||||
@interface SNTSyncRuleDownload : SNTSyncStage
|
||||
- (SNTRule *)ruleFromDictionary:(NSDictionary *)dict;
|
||||
@end
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "Source/common/SNTRule.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santasyncservice/SNTPushNotificationsTracker.h"
|
||||
#import "Source/santasyncservice/SNTSyncConstants.h"
|
||||
#import "Source/santasyncservice/SNTSyncLogging.h"
|
||||
#import "Source/santasyncservice/SNTSyncState.h"
|
||||
|
||||
@@ -92,8 +92,9 @@
|
||||
|
||||
uint32_t count = 0;
|
||||
for (NSDictionary *ruleDict in response[kRules]) {
|
||||
SNTRule *rule = [self ruleFromDictionary:ruleDict];
|
||||
SNTRule *rule = [[SNTRule alloc] initWithDictionary:ruleDict];
|
||||
if (rule) {
|
||||
[self processBundleNotificationsForRule:rule fromDictionary:ruleDict];
|
||||
[newRules addObject:rule];
|
||||
count++;
|
||||
}
|
||||
@@ -128,60 +129,14 @@
|
||||
[tracker removeNotificationsForHashes:processed];
|
||||
}
|
||||
|
||||
// Converts rule information downloaded from the server into a SNTRule. Because any information
|
||||
// not recorded by SNTRule is thrown away here, this method is also responsible for dealing with
|
||||
// the extra bundle rule information (bundle_hash & rule_count).
|
||||
- (SNTRule *)ruleFromDictionary:(NSDictionary *)dict {
|
||||
if (![dict isKindOfClass:[NSDictionary class]]) return nil;
|
||||
|
||||
SNTRule *newRule = [[SNTRule alloc] init];
|
||||
newRule.identifier = dict[kRuleIdentifier];
|
||||
if (newRule.identifier == nil) {
|
||||
newRule.identifier = dict[kRuleSHA256];
|
||||
}
|
||||
|
||||
NSString *policyString = dict[kRulePolicy];
|
||||
if ([policyString isEqual:kRulePolicyAllowlist] ||
|
||||
[policyString isEqual:kRulePolicyAllowlistDeprecated]) {
|
||||
newRule.state = SNTRuleStateAllow;
|
||||
} else if ([policyString isEqual:kRulePolicyAllowlistCompiler] ||
|
||||
[policyString isEqual:kRulePolicyAllowlistCompilerDeprecated]) {
|
||||
newRule.state = SNTRuleStateAllowCompiler;
|
||||
} else if ([policyString isEqual:kRulePolicyBlocklist] ||
|
||||
[policyString isEqual:kRulePolicyBlocklistDeprecated]) {
|
||||
newRule.state = SNTRuleStateBlock;
|
||||
} else if ([policyString isEqual:kRulePolicySilentBlocklist] ||
|
||||
[policyString isEqual:kRulePolicySilentBlocklistDeprecated]) {
|
||||
newRule.state = SNTRuleStateSilentBlock;
|
||||
} else if ([policyString isEqual:kRulePolicyRemove]) {
|
||||
newRule.state = SNTRuleStateRemove;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *ruleTypeString = dict[kRuleType];
|
||||
if ([ruleTypeString isEqual:kRuleTypeBinary]) {
|
||||
newRule.type = SNTRuleTypeBinary;
|
||||
} else if ([ruleTypeString isEqual:kRuleTypeCertificate]) {
|
||||
newRule.type = SNTRuleTypeCertificate;
|
||||
} else if ([ruleTypeString isEqual:kRuleTypeTeamID]) {
|
||||
newRule.type = SNTRuleTypeTeamID;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *customMsg = dict[kRuleCustomMsg];
|
||||
if (customMsg.length) {
|
||||
newRule.customMsg = customMsg;
|
||||
}
|
||||
|
||||
- (void)processBundleNotificationsForRule:(SNTRule *)rule fromDictionary:(NSDictionary *)dict {
|
||||
// Check rule for extra notification related info.
|
||||
if (newRule.state == SNTRuleStateAllow || newRule.state == SNTRuleStateAllowCompiler) {
|
||||
if (rule.state == SNTRuleStateAllow || rule.state == SNTRuleStateAllowCompiler) {
|
||||
// primaryHash is the bundle hash if there was a bundle hash included in the rule, otherwise
|
||||
// it is simply the binary hash.
|
||||
NSString *primaryHash = dict[kFileBundleHash];
|
||||
if (primaryHash.length != 64) {
|
||||
primaryHash = newRule.identifier;
|
||||
primaryHash = rule.identifier;
|
||||
}
|
||||
|
||||
// As we read in rules, we update the "remaining count" information. This count represents the
|
||||
@@ -190,8 +145,6 @@
|
||||
decrementPendingRulesForHash:primaryHash
|
||||
totalRuleCount:dict[kFileBundleBinaryCount]];
|
||||
}
|
||||
|
||||
return newRule;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santasyncservice/NSData+Zlib.h"
|
||||
#import "Source/santasyncservice/SNTSyncConstants.h"
|
||||
#import "Source/santasyncservice/SNTSyncLogging.h"
|
||||
#import "Source/santasyncservice/SNTSyncState.h"
|
||||
|
||||
@@ -199,9 +199,6 @@
|
||||
[self performRequest:request timeout:10 response:&response error:NULL];
|
||||
if (response.statusCode == 200) {
|
||||
NSDictionary *headers = [response allHeaderFields];
|
||||
[[self.daemonConn remoteObjectProxy] setXsrfToken:headers[kXSRFToken]
|
||||
reply:^{
|
||||
}];
|
||||
self.syncState.xsrfToken = headers[kXSRFToken];
|
||||
SLOGD(@"Retrieved new XSRF token");
|
||||
success = YES;
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTRule.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santasyncservice/SNTSyncConstants.h"
|
||||
#import "Source/santasyncservice/SNTSyncEventUpload.h"
|
||||
#import "Source/santasyncservice/SNTSyncPostflight.h"
|
||||
#import "Source/santasyncservice/SNTSyncPreflight.h"
|
||||
@@ -340,6 +340,8 @@
|
||||
XCTAssertEqualObjects(cert[kCertValidFrom], @(1365806075));
|
||||
XCTAssertEqualObjects(cert[kCertValidUntil], @(1618266875));
|
||||
|
||||
XCTAssertEqualObjects(event[kTeamID], @"012345678910");
|
||||
|
||||
event = events[1];
|
||||
XCTAssertEqualObjects(event[kFileName], @"hub");
|
||||
XCTAssertEqualObjects(event[kExecutingUser], @"foouser");
|
||||
@@ -466,4 +468,36 @@
|
||||
OCMVerify([self.daemonConnRop databaseRuleAddRules:rules cleanSlate:NO reply:OCMOCK_ANY]);
|
||||
}
|
||||
|
||||
#pragma mark - SNTSyncPostflight Tests
|
||||
|
||||
- (void)testPostflightBasicResponse {
|
||||
[self setupDefaultDaemonConnResponses];
|
||||
SNTSyncPostflight *sut = [[SNTSyncPostflight alloc] initWithState:self.syncState];
|
||||
|
||||
[self stubRequestBody:nil response:nil error:nil validateBlock:nil];
|
||||
|
||||
XCTAssertTrue([sut sync]);
|
||||
OCMVerify([self.daemonConnRop setFullSyncLastSuccess:OCMOCK_ANY reply:OCMOCK_ANY]);
|
||||
|
||||
self.syncState.clientMode = SNTClientModeMonitor;
|
||||
XCTAssertTrue([sut sync]);
|
||||
OCMVerify([self.daemonConnRop setClientMode:SNTClientModeMonitor reply:OCMOCK_ANY]);
|
||||
|
||||
self.syncState.cleanSync = YES;
|
||||
XCTAssertTrue([sut sync]);
|
||||
OCMVerify([self.daemonConnRop setSyncCleanRequired:NO reply:OCMOCK_ANY]);
|
||||
|
||||
self.syncState.allowlistRegex = @"^horse$";
|
||||
self.syncState.blocklistRegex = @"^donkey$";
|
||||
XCTAssertTrue([sut sync]);
|
||||
OCMVerify([self.daemonConnRop setAllowedPathRegex:@"^horse$" reply:OCMOCK_ANY]);
|
||||
OCMVerify([self.daemonConnRop setBlockedPathRegex:@"^donkey$" reply:OCMOCK_ANY]);
|
||||
|
||||
self.syncState.blockUSBMount = YES;
|
||||
self.syncState.remountUSBMode = @[ @"readonly" ];
|
||||
XCTAssertTrue([sut sync]);
|
||||
OCMVerify([self.daemonConnRop setBlockUSBMount:YES reply:OCMOCK_ANY]);
|
||||
OCMVerify([self.daemonConnRop setRemountUSBMode:@[ @"readonly" ] reply:OCMOCK_ANY]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -91,6 +91,11 @@
|
||||
<key>CF$UID</key>
|
||||
<integer>6</integer>
|
||||
</dict>
|
||||
<key>teamID</key>
|
||||
<dict>
|
||||
<key>CF$UID</key>
|
||||
<integer>39</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<integer>14887</integer>
|
||||
<string>ff98fa0c0a1095fedcbe4d388a9760e71399a5c3c017a847ffa545663b57929a</string>
|
||||
@@ -393,6 +398,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<string>012345678910</string>
|
||||
</array>
|
||||
<key>$top</key>
|
||||
<dict>
|
||||
|
||||
BIN
Source/santasyncservice/testdata/sync_preflight_basic.gz
vendored
Normal file
BIN
Source/santasyncservice/testdata/sync_preflight_basic.z
vendored
Normal file
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
# TODO: Pull benchmarks from previous commit to check for regression
|
||||
bazel test //:benchmarks --define=SANTA_BUILD_TYPE=ci
|
||||
bazel test //:benchmarks --define=SANTA_BUILD_TYPE=adhoc
|
||||
|
||||
@@ -7,7 +7,10 @@ function main() {
|
||||
find $GIT_ROOT \( -name "*.m" -o -name "*.h" -name "*.mm" \) -exec clang-format --Werror --dry-run {} \+
|
||||
err="$(( $err | $? ))"
|
||||
|
||||
go get github.com/bazelbuild/buildtools/buildifier
|
||||
! git grep -EIn $'[ \t]+$'
|
||||
err="$(( $err | $? ))"
|
||||
|
||||
go install github.com/bazelbuild/buildtools/buildifier@latest
|
||||
~/go/bin/buildifier --lint=warn -r $GIT_ROOT
|
||||
err="$(( $err | $? ))"
|
||||
return $err
|
||||
|
||||
13
WORKSPACE
@@ -5,15 +5,14 @@ load(
|
||||
"git_repository",
|
||||
"new_git_repository",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
name = "build_bazel_rules_apple",
|
||||
commit = "7115f0188d141d57d64a6875735847c975956dae", # 0.34.0
|
||||
remote = "https://github.com/bazelbuild/rules_apple.git",
|
||||
)
|
||||
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
http_archive(
|
||||
name = "build_bazel_rules_apple",
|
||||
sha256 = "36072d4f3614d309d6a703da0dfe48684ec4c65a89611aeb9590b45af7a3e592", # 1.0.1
|
||||
urls = ["https://github.com/bazelbuild/rules_apple/releases/download/1.0.1/rules_apple.1.0.1.tar.gz"],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "rules_proto_grpc",
|
||||
sha256 = "28724736b7ff49a48cb4b2b8cfa373f89edfcb9e8e492a8d5ab60aa3459314c8",
|
||||
|
||||
6
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
_site
|
||||
.sass-cache
|
||||
.jekyll-cache
|
||||
.jekyll-metadata
|
||||
vendor
|
||||
Gemfile.lock
|
||||
14
docs/Gemfile
Normal file
@@ -0,0 +1,14 @@
|
||||
source "https://rubygems.org"
|
||||
# Hello! This is where you manage which Jekyll version is used to run.
|
||||
# When you want to use a different version, change it below, save the
|
||||
# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
|
||||
#
|
||||
# bundle exec jekyll serve
|
||||
#
|
||||
gem "github-pages", group: :jekyll_plugins
|
||||
|
||||
# If you have any plugins, put them here!
|
||||
group :jekyll_plugins do
|
||||
gem "jekyll-feed", "~> 0.12"
|
||||
end
|
||||
gem "webrick", "~> 1.7"
|
||||
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
5
docs/binaries/index.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Binaries
|
||||
has_children: true
|
||||
nav_order: 5
|
||||
---
|
||||