Skip to content

Commit 70e266d

Browse files
santactl/sync: Add ability to ingest CEL rules. (#453)
This covers `santactl rule`, syncing, and StaticRules.
1 parent ad9b397 commit 70e266d

19 files changed

Lines changed: 157 additions & 32 deletions

Source/common/SNTRule.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@
8282
state:(SNTRuleState)state
8383
type:(SNTRuleType)type
8484
customMsg:(NSString *)customMsg
85-
customURL:(NSString *)customURL;
85+
customURL:(NSString *)customURL
86+
celExpr:(NSString *)celExpr;
8687

8788
///
8889
/// Initialize with a default timestamp: current time if rule state is transitive, 0 otherwise.

Source/common/SNTRule.m

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,15 +166,16 @@ - (instancetype)initWithIdentifier:(NSString *)identifier
166166
state:(SNTRuleState)state
167167
type:(SNTRuleType)type
168168
customMsg:(NSString *)customMsg
169-
customURL:(NSString *)customURL {
169+
customURL:(NSString *)customURL
170+
celExpr:(NSString *)celExpr {
170171
self = [self initWithIdentifier:identifier
171172
state:state
172173
type:type
173174
customMsg:customMsg
174175
customURL:customURL
175176
timestamp:0
176177
comment:nil
177-
celExpr:nil
178+
celExpr:celExpr
178179
error:nil];
179180
// Initialize timestamp to current time if rule is transitive.
180181
if (self && state == SNTRuleStateAllowTransitive) {
@@ -186,7 +187,15 @@ - (instancetype)initWithIdentifier:(NSString *)identifier
186187
- (instancetype)initWithIdentifier:(NSString *)identifier
187188
state:(SNTRuleState)state
188189
type:(SNTRuleType)type {
189-
return [self initWithIdentifier:identifier state:state type:type customMsg:nil customURL:nil];
190+
return [self initWithIdentifier:identifier
191+
state:state
192+
type:type
193+
customMsg:nil
194+
customURL:nil
195+
timestamp:0
196+
comment:nil
197+
celExpr:nil
198+
error:nil];
190199
}
191200

192201
// lowercase policy keys and upper case the policy decision.
@@ -254,6 +263,8 @@ - (instancetype)initWithDictionary:(NSDictionary *)rawDict error:(NSError **)err
254263
state = SNTRuleStateSilentBlock;
255264
} else if ([policyString isEqual:kRulePolicyRemove]) {
256265
state = SNTRuleStateRemove;
266+
} else if ([policyString isEqual:kRulePolicyCEL]) {
267+
state = SNTRuleStateCEL;
257268
} else {
258269
[SNTError populateError:error
259270
withCode:SNTErrorCodeRuleInvalidPolicy
@@ -301,14 +312,19 @@ - (instancetype)initWithDictionary:(NSDictionary *)rawDict error:(NSError **)err
301312
comment = nil;
302313
}
303314

315+
NSString *celExpr = dict[kRuleCELExpr];
316+
if (![celExpr isKindOfClass:[NSString class]] || celExpr.length == 0) {
317+
celExpr = nil;
318+
}
319+
304320
return [self initWithIdentifier:identifier
305321
state:state
306322
type:type
307323
customMsg:customMsg
308324
customURL:customURL
309325
timestamp:0
310326
comment:comment
311-
celExpr:nil
327+
celExpr:celExpr
312328
error:error];
313329
}
314330

@@ -354,6 +370,7 @@ - (NSString *)ruleStateToPolicyString:(SNTRuleState)state {
354370
case SNTRuleStateAllowTransitive: return @"AllowTransitive";
355371
case SNTRuleStateAllowLocalBinary: return kRulePolicyAllowlistLocalBinary;
356372
case SNTRuleStateAllowLocalSigningID: return kRulePolicyAllowlistLocalSigningID;
373+
case SNTRuleStateCEL: return kRulePolicyCEL;
357374
// This should never be hit. But is here for completion.
358375
default: return @"Unknown";
359376
}
@@ -381,6 +398,7 @@ - (NSDictionary *)dictionaryRepresentation {
381398
kRuleCustomMsg : self.customMsg ?: @"",
382399
kRuleCustomURL : self.customURL ?: @"",
383400
kRuleComment : self.comment ?: @"",
401+
kRuleCELExpr : self.celExpr ?: @"",
384402
};
385403
}
386404

@@ -399,6 +417,7 @@ - (NSUInteger)hash {
399417
result = prime * result + self.state;
400418
result = prime * result + self.type;
401419
result = prime * result + [self.celExpr hash];
420+
result = prime * result + [self.celExpr hash];
402421
return result;
403422
}
404423

Source/common/SNTRuleTest.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ - (void)testRuleDictionaryRepresentation {
285285
@"custom_msg" : @"A custom block message",
286286
@"custom_url" : @"https://example.com",
287287
@"comment" : @"",
288+
@"cel_expr" : @"",
288289
};
289290

290291
SNTRule *sut = [[SNTRule alloc] initWithDictionary:expectedTeamID error:nil];
@@ -298,6 +299,7 @@ - (void)testRuleDictionaryRepresentation {
298299
@"custom_msg" : @"",
299300
@"custom_url" : @"",
300301
@"comment" : @"",
302+
@"cel_expr" : @"",
301303
};
302304

303305
sut = [[SNTRule alloc] initWithDictionary:expectedBinary error:nil];
@@ -313,6 +315,7 @@ - (void)testRuleStateToPolicyString {
313315
@"rule_type" : @"BINARY",
314316
@"custom_msg" : @"A custom block message",
315317
@"custom_url" : @"https://example.com",
318+
@"cel_expr" : @"",
316319
};
317320

318321
SNTRule *sut = [[SNTRule alloc] initWithDictionary:expected error:nil];
@@ -333,6 +336,7 @@ - (void)testKeyCaseForInitWithDictionary {
333336
for (NSString *key in
334337
@[ kRulePolicy, kRuleIdentifier, kRuleType, kRuleCustomMsg, kRuleCustomURL, kRuleComment ]) {
335338
NSDictionary *expected = @{
339+
@"cel_expr" : @"",
336340
@"identifier" : @"84de9c61777ca36b13228e2446d53e966096e78db7a72c632b5c185b2ffe68a6",
337341
@"policy" : @"ALLOWLIST",
338342
@"rule_type" : @"BINARY",

Source/common/SNTSyncConstants.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ extern NSString *const kRulePolicyBlocklistDeprecated;
126126
extern NSString *const kRulePolicySilentBlocklist;
127127
extern NSString *const kRulePolicySilentBlocklistDeprecated;
128128
extern NSString *const kRulePolicyRemove;
129+
extern NSString *const kRulePolicyCEL;
129130
extern NSString *const kRuleType;
130131
extern NSString *const kRuleTypeBinary;
131132
extern NSString *const kRuleTypeCertificate;
@@ -135,6 +136,7 @@ extern NSString *const kRuleTypeCDHash;
135136
extern NSString *const kRuleCustomMsg;
136137
extern NSString *const kRuleCustomURL;
137138
extern NSString *const kRuleComment;
139+
extern NSString *const kRuleCELExpr;
138140
extern NSString *const kCursor;
139141

140142
extern NSString *const kBackoffInterval;

Source/common/SNTSyncConstants.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
NSString *const kRulePolicySilentBlocklist = @"SILENT_BLOCKLIST";
128128
NSString *const kRulePolicySilentBlocklistDeprecated = @"SILENT_BLACKLIST";
129129
NSString *const kRulePolicyRemove = @"REMOVE";
130+
NSString *const kRulePolicyCEL = @"CEL";
130131
NSString *const kRuleType = @"rule_type";
131132
NSString *const kRuleTypeBinary = @"BINARY";
132133
NSString *const kRuleTypeCertificate = @"CERTIFICATE";
@@ -136,6 +137,7 @@
136137
NSString *const kRuleCustomMsg = @"custom_msg";
137138
NSString *const kRuleCustomURL = @"custom_url";
138139
NSString *const kRuleComment = @"comment";
140+
NSString *const kRuleCELExpr = @"cel_expr";
139141
NSString *const kCursor = @"cursor";
140142

141143
NSString *const kBackoffInterval = @"backoff";

Source/common/cel/Activation.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,23 @@
3232
#include "eval/public/activation.h"
3333
#pragma clang diagnostic pop
3434

35-
namespace cel_runtime = ::google::api::expr::runtime;
36-
namespace pbv1 = ::santa::cel::v1;
37-
3835
namespace santa {
3936
namespace cel {
4037

4138
// SantaActivation is a CEL activation that provides lookups of values from the
4239
// santa.pb.cel.v1.Context message, and easy access to variables for return values.
43-
class Activation : public ::cel_runtime::BaseActivation {
40+
class Activation : public ::google::api::expr::runtime::BaseActivation {
4441
public:
45-
Activation(std::unique_ptr<::pbv1::ExecutableFile> file, std::vector<std::string> (^args)(),
46-
std::map<std::string, std::string> (^envs)())
42+
Activation(std::unique_ptr<::santa::cel::v1::ExecutableFile> file,
43+
std::vector<std::string> (^args)(), std::map<std::string, std::string> (^envs)())
4744
: file_(std::move(file)), args_(args), envs_(envs) {};
4845
~Activation() = default;
4946

50-
std::optional<cel_runtime::CelValue> FindValue(absl::string_view name,
51-
google::protobuf::Arena *arena) const override;
47+
std::optional<::google::api::expr::runtime::CelValue> FindValue(
48+
absl::string_view name, google::protobuf::Arena *arena) const override;
5249

5350
// Activation does not support lazy-loaded functions.
54-
std::vector<const cel_runtime::CelFunction *> FindFunctionOverloads(
51+
std::vector<const ::google::api::expr::runtime::CelFunction *> FindFunctionOverloads(
5552
absl::string_view) const override {
5653
return {};
5754
}
@@ -72,11 +69,14 @@ class Activation : public ::cel_runtime::BaseActivation {
7269
const google::protobuf::Descriptor *messageType);
7370

7471
template <typename T>
75-
static cel_runtime::CelValue CELValue(const T &v, google::protobuf::Arena *arena);
72+
static ::google::api::expr::runtime::CelValue CELValue(const T &v,
73+
google::protobuf::Arena *arena);
7674
template <typename T>
77-
static cel_runtime::CelValue CELValue(const std::vector<T> &v, google::protobuf::Arena *arena);
75+
static ::google::api::expr::runtime::CelValue CELValue(const std::vector<T> &v,
76+
google::protobuf::Arena *arena);
7877
template <typename K, typename V>
79-
static cel_runtime::CelValue CELValue(const std::map<K, V> &v, google::protobuf::Arena *arena);
78+
static ::google::api::expr::runtime::CelValue CELValue(const std::map<K, V> &v,
79+
google::protobuf::Arena *arena);
8080
};
8181

8282
} // namespace cel

Source/common/cel/Activation.mm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
std::optional<cel_runtime::CelValue> Activation::FindValue(absl::string_view name,
8585
google::protobuf::Arena *arena) const {
8686
// Handle the ReturnValue values.
87-
auto retDescriptor = pbv1::ReturnValue_descriptor();
87+
auto retDescriptor = santa::cel::v1::ReturnValue_descriptor();
8888
auto retValue = retDescriptor->FindValueByName(name);
8989
if (retValue != nullptr) {
9090
return CELValue(retValue->number(), arena);
@@ -109,14 +109,14 @@
109109
// ALLOWLIST or BLOCKLIST in their CEL expressions without having to use the
110110
// proto package name prefix. Start from value number 1 to avoid the
111111
// UNSPECIFIED value.
112-
auto retDescriptor = pbv1::ReturnValue_descriptor();
112+
auto retDescriptor = ::santa::cel::v1::ReturnValue_descriptor();
113113
for (int i = 1; i < retDescriptor->value_count(); i++) {
114114
auto value = retDescriptor->value(i);
115115
v.push_back({value->name(), ::cel::IntType()});
116116
}
117117

118118
// Now add all the fields from the CELContext message.
119-
auto ctxDescriptor = pbv1::ExecutionContext::descriptor();
119+
auto ctxDescriptor = santa::cel::v1::ExecutionContext::descriptor();
120120
for (int i = 0; i < ctxDescriptor->field_count(); i++) {
121121
auto field = ctxDescriptor->field(i);
122122

Source/santactl/Commands/SNTCommandRule.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ + (NSString *)longHelpText {
5454
@" --block: add to block\n"
5555
@" --silent-block: add to silent block\n"
5656
@" --compiler: allow and mark as a compiler\n"
57+
@" --cel {cel_expr}: add a CEL rule\n"
58+
@" See https://northpole.dev/features/binary-authorization#cel for more "
59+
@"information.\n"
5760
@" --remove: remove existing rule\n"
5861
@" --check: check for an existing rule\n"
5962
@" --import {path}: import rules from a JSON file\n"
@@ -153,6 +156,12 @@ - (void)runWithArguments:(NSArray *)arguments {
153156
newRule.state = SNTRuleStateSilentBlock;
154157
} else if ([arg caseInsensitiveCompare:@"--compiler"] == NSOrderedSame) {
155158
newRule.state = SNTRuleStateAllowCompiler;
159+
} else if ([arg caseInsensitiveCompare:@"--cel"] == NSOrderedSame) {
160+
if (++i > arguments.count - 1) {
161+
[self printErrorUsageAndExit:@"--cel requires an argument"];
162+
}
163+
newRule.state = SNTRuleStateCEL;
164+
newRule.celExpr = arguments[i];
156165
} else if ([arg caseInsensitiveCompare:@"--remove"] == NSOrderedSame) {
157166
newRule.state = SNTRuleStateRemove;
158167
} else if ([arg caseInsensitiveCompare:@"--check"] == NSOrderedSame) {

Source/santad/SNTPolicyProcessor.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ - (BOOL)decision:(SNTCachedDecision *)cd
127127
break;
128128
default: break;
129129
}
130-
if (!(*evalResult).second) {
130+
if (!evalResult->second) {
131131
cd.cacheable = NO;
132132
}
133133
}

Source/santasyncservice/BUILD

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ licenses(["notice"])
66

77
objc_library(
88
name = "FCM_lib",
9-
srcs = ["SNTSyncFCM.m"],
9+
srcs = ["SNTSyncFCM.mm"],
1010
hdrs = ["SNTSyncFCM.h"],
1111
sdk_frameworks = ["Network"],
1212
deps = [
@@ -30,11 +30,12 @@ objc_library(
3030

3131
objc_library(
3232
name = "SNTSyncState",
33-
srcs = ["SNTSyncState.m"],
33+
srcs = ["SNTSyncState.mm"],
3434
hdrs = ["SNTSyncState.h"],
3535
deps = [
3636
"//Source/common:SNTCommonEnums",
3737
"//Source/common:SNTExportConfiguration",
38+
"//Source/common/cel:CEL",
3839
],
3940
)
4041

@@ -73,17 +74,17 @@ objc_library(
7374
"NSData+Zlib.h",
7475
"NSData+Zlib.m",
7576
"SNTPushClientAPNS.h",
76-
"SNTPushClientAPNS.m",
77+
"SNTPushClientAPNS.mm",
7778
"SNTPushClientFCM.h",
78-
"SNTPushClientFCM.m",
79+
"SNTPushClientFCM.mm",
7980
"SNTPushNotifications.h",
8081
"SNTPushNotificationsTracker.h",
8182
"SNTPushNotificationsTracker.m",
8283
"SNTSyncEventUpload.h",
8384
"SNTSyncEventUpload.mm",
8485
"SNTSyncLogging.h",
8586
"SNTSyncLogging.m",
86-
"SNTSyncManager.m",
87+
"SNTSyncManager.mm",
8788
"SNTSyncPostflight.h",
8889
"SNTSyncPostflight.mm",
8990
"SNTSyncPreflight.h",
@@ -124,6 +125,7 @@ objc_library(
124125
"//Source/common:SNTXPCControlInterface",
125126
"//Source/common:SNTXPCSyncServiceInterface",
126127
"//Source/common:String",
128+
"//Source/common/cel:CEL",
127129
"@northpolesec_protos//sync:v1_cc_proto",
128130
"@protobuf//src/google/protobuf/json",
129131
],
@@ -138,9 +140,9 @@ santa_unit_test(
138140
"NSData+Zlib.h",
139141
"NSData+Zlib.m",
140142
"SNTPushClientAPNS.h",
141-
"SNTPushClientAPNS.m",
143+
"SNTPushClientAPNS.mm",
142144
"SNTPushClientFCM.h",
143-
"SNTPushClientFCM.m",
145+
"SNTPushClientFCM.mm",
144146
"SNTPushNotifications.h",
145147
"SNTPushNotificationsTracker.h",
146148
"SNTPushNotificationsTracker.m",
@@ -185,6 +187,7 @@ santa_unit_test(
185187
"//Source/common:SNTSystemInfo",
186188
"//Source/common:SNTXPCControlInterface",
187189
"//Source/common:String",
190+
"//Source/common/cel:CEL",
188191
"@OCMock",
189192
"@northpolesec_protos//sync:v1_cc_proto",
190193
"@protobuf//src/google/protobuf/json",

0 commit comments

Comments
 (0)