diff --git a/Source/common/SNTRule.h b/Source/common/SNTRule.h index 3b6b93f73..b0ce80631 100644 --- a/Source/common/SNTRule.h +++ b/Source/common/SNTRule.h @@ -82,7 +82,8 @@ state:(SNTRuleState)state type:(SNTRuleType)type customMsg:(NSString *)customMsg - customURL:(NSString *)customURL; + customURL:(NSString *)customURL + celExpr:(NSString *)celExpr; /// /// Initialize with a default timestamp: current time if rule state is transitive, 0 otherwise. diff --git a/Source/common/SNTRule.m b/Source/common/SNTRule.m index d602768f0..7d2946ae9 100644 --- a/Source/common/SNTRule.m +++ b/Source/common/SNTRule.m @@ -166,7 +166,8 @@ - (instancetype)initWithIdentifier:(NSString *)identifier state:(SNTRuleState)state type:(SNTRuleType)type customMsg:(NSString *)customMsg - customURL:(NSString *)customURL { + customURL:(NSString *)customURL + celExpr:(NSString *)celExpr { self = [self initWithIdentifier:identifier state:state type:type @@ -174,7 +175,7 @@ - (instancetype)initWithIdentifier:(NSString *)identifier customURL:customURL timestamp:0 comment:nil - celExpr:nil + celExpr:celExpr error:nil]; // Initialize timestamp to current time if rule is transitive. if (self && state == SNTRuleStateAllowTransitive) { @@ -186,7 +187,15 @@ - (instancetype)initWithIdentifier:(NSString *)identifier - (instancetype)initWithIdentifier:(NSString *)identifier state:(SNTRuleState)state type:(SNTRuleType)type { - return [self initWithIdentifier:identifier state:state type:type customMsg:nil customURL:nil]; + return [self initWithIdentifier:identifier + state:state + type:type + customMsg:nil + customURL:nil + timestamp:0 + comment:nil + celExpr:nil + error:nil]; } // lowercase policy keys and upper case the policy decision. @@ -254,6 +263,8 @@ - (instancetype)initWithDictionary:(NSDictionary *)rawDict error:(NSError **)err state = SNTRuleStateSilentBlock; } else if ([policyString isEqual:kRulePolicyRemove]) { state = SNTRuleStateRemove; + } else if ([policyString isEqual:kRulePolicyCEL]) { + state = SNTRuleStateCEL; } else { [SNTError populateError:error withCode:SNTErrorCodeRuleInvalidPolicy @@ -301,6 +312,11 @@ - (instancetype)initWithDictionary:(NSDictionary *)rawDict error:(NSError **)err comment = nil; } + NSString *celExpr = dict[kRuleCELExpr]; + if (![celExpr isKindOfClass:[NSString class]] || celExpr.length == 0) { + celExpr = nil; + } + return [self initWithIdentifier:identifier state:state type:type @@ -308,7 +324,7 @@ - (instancetype)initWithDictionary:(NSDictionary *)rawDict error:(NSError **)err customURL:customURL timestamp:0 comment:comment - celExpr:nil + celExpr:celExpr error:error]; } @@ -354,6 +370,7 @@ - (NSString *)ruleStateToPolicyString:(SNTRuleState)state { case SNTRuleStateAllowTransitive: return @"AllowTransitive"; case SNTRuleStateAllowLocalBinary: return kRulePolicyAllowlistLocalBinary; case SNTRuleStateAllowLocalSigningID: return kRulePolicyAllowlistLocalSigningID; + case SNTRuleStateCEL: return kRulePolicyCEL; // This should never be hit. But is here for completion. default: return @"Unknown"; } @@ -381,6 +398,7 @@ - (NSDictionary *)dictionaryRepresentation { kRuleCustomMsg : self.customMsg ?: @"", kRuleCustomURL : self.customURL ?: @"", kRuleComment : self.comment ?: @"", + kRuleCELExpr : self.celExpr ?: @"", }; } @@ -399,6 +417,7 @@ - (NSUInteger)hash { result = prime * result + self.state; result = prime * result + self.type; result = prime * result + [self.celExpr hash]; + result = prime * result + [self.celExpr hash]; return result; } diff --git a/Source/common/SNTRuleTest.mm b/Source/common/SNTRuleTest.mm index 96136f5cf..7b5fdfe03 100644 --- a/Source/common/SNTRuleTest.mm +++ b/Source/common/SNTRuleTest.mm @@ -285,6 +285,7 @@ - (void)testRuleDictionaryRepresentation { @"custom_msg" : @"A custom block message", @"custom_url" : @"https://example.com", @"comment" : @"", + @"cel_expr" : @"", }; SNTRule *sut = [[SNTRule alloc] initWithDictionary:expectedTeamID error:nil]; @@ -298,6 +299,7 @@ - (void)testRuleDictionaryRepresentation { @"custom_msg" : @"", @"custom_url" : @"", @"comment" : @"", + @"cel_expr" : @"", }; sut = [[SNTRule alloc] initWithDictionary:expectedBinary error:nil]; @@ -313,6 +315,7 @@ - (void)testRuleStateToPolicyString { @"rule_type" : @"BINARY", @"custom_msg" : @"A custom block message", @"custom_url" : @"https://example.com", + @"cel_expr" : @"", }; SNTRule *sut = [[SNTRule alloc] initWithDictionary:expected error:nil]; @@ -333,6 +336,7 @@ - (void)testKeyCaseForInitWithDictionary { for (NSString *key in @[ kRulePolicy, kRuleIdentifier, kRuleType, kRuleCustomMsg, kRuleCustomURL, kRuleComment ]) { NSDictionary *expected = @{ + @"cel_expr" : @"", @"identifier" : @"84de9c61777ca36b13228e2446d53e966096e78db7a72c632b5c185b2ffe68a6", @"policy" : @"ALLOWLIST", @"rule_type" : @"BINARY", diff --git a/Source/common/SNTSyncConstants.h b/Source/common/SNTSyncConstants.h index bcf1b0bf5..4393167b8 100644 --- a/Source/common/SNTSyncConstants.h +++ b/Source/common/SNTSyncConstants.h @@ -126,6 +126,7 @@ extern NSString *const kRulePolicyBlocklistDeprecated; extern NSString *const kRulePolicySilentBlocklist; extern NSString *const kRulePolicySilentBlocklistDeprecated; extern NSString *const kRulePolicyRemove; +extern NSString *const kRulePolicyCEL; extern NSString *const kRuleType; extern NSString *const kRuleTypeBinary; extern NSString *const kRuleTypeCertificate; @@ -135,6 +136,7 @@ extern NSString *const kRuleTypeCDHash; extern NSString *const kRuleCustomMsg; extern NSString *const kRuleCustomURL; extern NSString *const kRuleComment; +extern NSString *const kRuleCELExpr; extern NSString *const kCursor; extern NSString *const kBackoffInterval; diff --git a/Source/common/SNTSyncConstants.m b/Source/common/SNTSyncConstants.m index 78b6cb698..590c76e73 100644 --- a/Source/common/SNTSyncConstants.m +++ b/Source/common/SNTSyncConstants.m @@ -127,6 +127,7 @@ NSString *const kRulePolicySilentBlocklist = @"SILENT_BLOCKLIST"; NSString *const kRulePolicySilentBlocklistDeprecated = @"SILENT_BLACKLIST"; NSString *const kRulePolicyRemove = @"REMOVE"; +NSString *const kRulePolicyCEL = @"CEL"; NSString *const kRuleType = @"rule_type"; NSString *const kRuleTypeBinary = @"BINARY"; NSString *const kRuleTypeCertificate = @"CERTIFICATE"; @@ -136,6 +137,7 @@ NSString *const kRuleCustomMsg = @"custom_msg"; NSString *const kRuleCustomURL = @"custom_url"; NSString *const kRuleComment = @"comment"; +NSString *const kRuleCELExpr = @"cel_expr"; NSString *const kCursor = @"cursor"; NSString *const kBackoffInterval = @"backoff"; diff --git a/Source/common/cel/Activation.h b/Source/common/cel/Activation.h index f1e63c4c0..855fb46e0 100644 --- a/Source/common/cel/Activation.h +++ b/Source/common/cel/Activation.h @@ -32,26 +32,23 @@ #include "eval/public/activation.h" #pragma clang diagnostic pop -namespace cel_runtime = ::google::api::expr::runtime; -namespace pbv1 = ::santa::cel::v1; - namespace santa { namespace cel { // SantaActivation is a CEL activation that provides lookups of values from the // santa.pb.cel.v1.Context message, and easy access to variables for return values. -class Activation : public ::cel_runtime::BaseActivation { +class Activation : public ::google::api::expr::runtime::BaseActivation { public: - Activation(std::unique_ptr<::pbv1::ExecutableFile> file, std::vector (^args)(), - std::map (^envs)()) + Activation(std::unique_ptr<::santa::cel::v1::ExecutableFile> file, + std::vector (^args)(), std::map (^envs)()) : file_(std::move(file)), args_(args), envs_(envs) {}; ~Activation() = default; - std::optional FindValue(absl::string_view name, - google::protobuf::Arena *arena) const override; + std::optional<::google::api::expr::runtime::CelValue> FindValue( + absl::string_view name, google::protobuf::Arena *arena) const override; // Activation does not support lazy-loaded functions. - std::vector FindFunctionOverloads( + std::vector FindFunctionOverloads( absl::string_view) const override { return {}; } @@ -72,11 +69,14 @@ class Activation : public ::cel_runtime::BaseActivation { const google::protobuf::Descriptor *messageType); template - static cel_runtime::CelValue CELValue(const T &v, google::protobuf::Arena *arena); + static ::google::api::expr::runtime::CelValue CELValue(const T &v, + google::protobuf::Arena *arena); template - static cel_runtime::CelValue CELValue(const std::vector &v, google::protobuf::Arena *arena); + static ::google::api::expr::runtime::CelValue CELValue(const std::vector &v, + google::protobuf::Arena *arena); template - static cel_runtime::CelValue CELValue(const std::map &v, google::protobuf::Arena *arena); + static ::google::api::expr::runtime::CelValue CELValue(const std::map &v, + google::protobuf::Arena *arena); }; } // namespace cel diff --git a/Source/common/cel/Activation.mm b/Source/common/cel/Activation.mm index 8b1390ad2..2fae31b56 100644 --- a/Source/common/cel/Activation.mm +++ b/Source/common/cel/Activation.mm @@ -84,7 +84,7 @@ std::optional Activation::FindValue(absl::string_view name, google::protobuf::Arena *arena) const { // Handle the ReturnValue values. - auto retDescriptor = pbv1::ReturnValue_descriptor(); + auto retDescriptor = santa::cel::v1::ReturnValue_descriptor(); auto retValue = retDescriptor->FindValueByName(name); if (retValue != nullptr) { return CELValue(retValue->number(), arena); @@ -109,14 +109,14 @@ // ALLOWLIST or BLOCKLIST in their CEL expressions without having to use the // proto package name prefix. Start from value number 1 to avoid the // UNSPECIFIED value. - auto retDescriptor = pbv1::ReturnValue_descriptor(); + auto retDescriptor = ::santa::cel::v1::ReturnValue_descriptor(); for (int i = 1; i < retDescriptor->value_count(); i++) { auto value = retDescriptor->value(i); v.push_back({value->name(), ::cel::IntType()}); } // Now add all the fields from the CELContext message. - auto ctxDescriptor = pbv1::ExecutionContext::descriptor(); + auto ctxDescriptor = santa::cel::v1::ExecutionContext::descriptor(); for (int i = 0; i < ctxDescriptor->field_count(); i++) { auto field = ctxDescriptor->field(i); diff --git a/Source/santactl/Commands/SNTCommandRule.m b/Source/santactl/Commands/SNTCommandRule.m index db18abcb5..6fcf0649a 100644 --- a/Source/santactl/Commands/SNTCommandRule.m +++ b/Source/santactl/Commands/SNTCommandRule.m @@ -54,6 +54,9 @@ + (NSString *)longHelpText { @" --block: add to block\n" @" --silent-block: add to silent block\n" @" --compiler: allow and mark as a compiler\n" + @" --cel {cel_expr}: add a CEL rule\n" + @" See https://northpole.dev/features/binary-authorization#cel for more " + @"information.\n" @" --remove: remove existing rule\n" @" --check: check for an existing rule\n" @" --import {path}: import rules from a JSON file\n" @@ -153,6 +156,12 @@ - (void)runWithArguments:(NSArray *)arguments { newRule.state = SNTRuleStateSilentBlock; } else if ([arg caseInsensitiveCompare:@"--compiler"] == NSOrderedSame) { newRule.state = SNTRuleStateAllowCompiler; + } else if ([arg caseInsensitiveCompare:@"--cel"] == NSOrderedSame) { + if (++i > arguments.count - 1) { + [self printErrorUsageAndExit:@"--cel requires an argument"]; + } + newRule.state = SNTRuleStateCEL; + newRule.celExpr = arguments[i]; } else if ([arg caseInsensitiveCompare:@"--remove"] == NSOrderedSame) { newRule.state = SNTRuleStateRemove; } else if ([arg caseInsensitiveCompare:@"--check"] == NSOrderedSame) { diff --git a/Source/santad/SNTPolicyProcessor.mm b/Source/santad/SNTPolicyProcessor.mm index 05c279b3d..7197cbcb0 100644 --- a/Source/santad/SNTPolicyProcessor.mm +++ b/Source/santad/SNTPolicyProcessor.mm @@ -127,7 +127,7 @@ - (BOOL)decision:(SNTCachedDecision *)cd break; default: break; } - if (!(*evalResult).second) { + if (!evalResult->second) { cd.cacheable = NO; } } diff --git a/Source/santasyncservice/BUILD b/Source/santasyncservice/BUILD index e37d8ec82..d3624e512 100644 --- a/Source/santasyncservice/BUILD +++ b/Source/santasyncservice/BUILD @@ -6,7 +6,7 @@ licenses(["notice"]) objc_library( name = "FCM_lib", - srcs = ["SNTSyncFCM.m"], + srcs = ["SNTSyncFCM.mm"], hdrs = ["SNTSyncFCM.h"], sdk_frameworks = ["Network"], deps = [ @@ -30,11 +30,12 @@ objc_library( objc_library( name = "SNTSyncState", - srcs = ["SNTSyncState.m"], + srcs = ["SNTSyncState.mm"], hdrs = ["SNTSyncState.h"], deps = [ "//Source/common:SNTCommonEnums", "//Source/common:SNTExportConfiguration", + "//Source/common/cel:CEL", ], ) @@ -73,9 +74,9 @@ objc_library( "NSData+Zlib.h", "NSData+Zlib.m", "SNTPushClientAPNS.h", - "SNTPushClientAPNS.m", + "SNTPushClientAPNS.mm", "SNTPushClientFCM.h", - "SNTPushClientFCM.m", + "SNTPushClientFCM.mm", "SNTPushNotifications.h", "SNTPushNotificationsTracker.h", "SNTPushNotificationsTracker.m", @@ -83,7 +84,7 @@ objc_library( "SNTSyncEventUpload.mm", "SNTSyncLogging.h", "SNTSyncLogging.m", - "SNTSyncManager.m", + "SNTSyncManager.mm", "SNTSyncPostflight.h", "SNTSyncPostflight.mm", "SNTSyncPreflight.h", @@ -124,6 +125,7 @@ objc_library( "//Source/common:SNTXPCControlInterface", "//Source/common:SNTXPCSyncServiceInterface", "//Source/common:String", + "//Source/common/cel:CEL", "@northpolesec_protos//sync:v1_cc_proto", "@protobuf//src/google/protobuf/json", ], @@ -138,9 +140,9 @@ santa_unit_test( "NSData+Zlib.h", "NSData+Zlib.m", "SNTPushClientAPNS.h", - "SNTPushClientAPNS.m", + "SNTPushClientAPNS.mm", "SNTPushClientFCM.h", - "SNTPushClientFCM.m", + "SNTPushClientFCM.mm", "SNTPushNotifications.h", "SNTPushNotificationsTracker.h", "SNTPushNotificationsTracker.m", @@ -185,6 +187,7 @@ santa_unit_test( "//Source/common:SNTSystemInfo", "//Source/common:SNTXPCControlInterface", "//Source/common:String", + "//Source/common/cel:CEL", "@OCMock", "@northpolesec_protos//sync:v1_cc_proto", "@protobuf//src/google/protobuf/json", diff --git a/Source/santasyncservice/SNTPushClientAPNS.m b/Source/santasyncservice/SNTPushClientAPNS.mm similarity index 100% rename from Source/santasyncservice/SNTPushClientAPNS.m rename to Source/santasyncservice/SNTPushClientAPNS.mm diff --git a/Source/santasyncservice/SNTPushClientFCM.m b/Source/santasyncservice/SNTPushClientFCM.mm similarity index 100% rename from Source/santasyncservice/SNTPushClientFCM.m rename to Source/santasyncservice/SNTPushClientFCM.mm diff --git a/Source/santasyncservice/SNTSyncFCM.m b/Source/santasyncservice/SNTSyncFCM.mm similarity index 100% rename from Source/santasyncservice/SNTSyncFCM.m rename to Source/santasyncservice/SNTSyncFCM.mm diff --git a/Source/santasyncservice/SNTSyncManager.m b/Source/santasyncservice/SNTSyncManager.mm similarity index 98% rename from Source/santasyncservice/SNTSyncManager.m rename to Source/santasyncservice/SNTSyncManager.mm index 110684b92..805d93d73 100644 --- a/Source/santasyncservice/SNTSyncManager.m +++ b/Source/santasyncservice/SNTSyncManager.mm @@ -26,6 +26,7 @@ #import "Source/common/SNTStrengthify.h" #import "Source/common/SNTSyncConstants.h" #import "Source/common/SNTXPCControlInterface.h" +#include "Source/common/cel/Evaluator.h" #import "Source/santasyncservice/SNTPushClientAPNS.h" #import "Source/santasyncservice/SNTPushClientFCM.h" #import "Source/santasyncservice/SNTPushNotifications.h" @@ -62,7 +63,9 @@ @interface SNTSyncManager () @end -@implementation SNTSyncManager +@implementation SNTSyncManager { + std::unique_ptr _celEvaluator; +} #pragma mark init @@ -93,6 +96,11 @@ - (instancetype)initWithDaemonConnection:(MOLXPCConnection *)daemonConn { _syncLimiter = dispatch_semaphore_create(kMaxEnqueuedSyncs); _eventBatchSize = kDefaultEventBatchSize; + + auto celEvaluator = santa::cel::Evaluator::Create(); + if (celEvaluator.ok()) { + _celEvaluator = std::move(celEvaluator.value()); + } } return self; } @@ -441,6 +449,8 @@ - (SNTSyncState *)createSyncStateWithStatus:(SNTSyncStatusType *)status { syncState.contentEncoding = config.syncClientContentEncoding; syncState.pushNotificationsToken = self.pushNotifications.token; + syncState.celEvaluator = _celEvaluator.get(); + return syncState; } diff --git a/Source/santasyncservice/SNTSyncRuleDownload.mm b/Source/santasyncservice/SNTSyncRuleDownload.mm index 27d719a9a..ee58519e6 100644 --- a/Source/santasyncservice/SNTSyncRuleDownload.mm +++ b/Source/santasyncservice/SNTSyncRuleDownload.mm @@ -21,6 +21,7 @@ #import "Source/common/SNTSyncConstants.h" #import "Source/common/SNTXPCControlInterface.h" #import "Source/common/String.h" +#include "Source/common/cel/Evaluator.h" #import "Source/santasyncservice/SNTPushNotificationsTracker.h" #import "Source/santasyncservice/SNTSyncConfigBundle.h" #import "Source/santasyncservice/SNTSyncLogging.h" @@ -162,6 +163,7 @@ - (SNTRule *)ruleFromProtoRule:(::pbv1::Rule)rule { case ::pbv1::BLOCKLIST: state = SNTRuleStateBlock; break; case ::pbv1::SILENT_BLOCKLIST: state = SNTRuleStateSilentBlock; break; case ::pbv1::REMOVE: state = SNTRuleStateRemove; break; + case ::pbv1::CEL: state = SNTRuleStateCEL; break; default: LOGE(@"Failed to process rule with unknown policy: %d", rule.policy()); return nil; } @@ -181,11 +183,22 @@ - (SNTRule *)ruleFromProtoRule:(::pbv1::Rule)rule { const std::string &custom_url = rule.custom_url(); NSString *customURL = (!custom_url.empty()) ? StringToNSString(custom_url) : nil; + const std::string &cel_expr = rule.cel_expr(); + NSString *celExpr = (!cel_expr.empty()) ? StringToNSString(cel_expr) : nil; + if (celExpr && self.syncState.celEvaluator) { + auto result = self.syncState.celEvaluator->Compile(cel_expr); + if (!result.ok()) { + LOGE(@"Failed to compile CEL expression: %s", std::string(result.status().message()).c_str()); + return nil; + } + } + return [[SNTRule alloc] initWithIdentifier:identifier state:state type:type customMsg:customMsg - customURL:customURL]; + customURL:customURL + celExpr:celExpr]; } // Send out push notifications for allowed bundles/binaries whose rule download was preceded by diff --git a/Source/santasyncservice/SNTSyncState.h b/Source/santasyncservice/SNTSyncState.h index 6a7750b1e..14e9c3a7f 100644 --- a/Source/santasyncservice/SNTSyncState.h +++ b/Source/santasyncservice/SNTSyncState.h @@ -17,6 +17,7 @@ #import "Source/common/SNTCommonEnums.h" #import "Source/common/SNTExportConfiguration.h" +#include "Source/common/cel/Evaluator.h" @class SNTSyncManager; @class MOLXPCConnection; @@ -93,4 +94,6 @@ @property BOOL preflightOnly; @property BOOL pushNotificationSync; +@property santa::cel::Evaluator *celEvaluator; + @end diff --git a/Source/santasyncservice/SNTSyncState.m b/Source/santasyncservice/SNTSyncState.mm similarity index 100% rename from Source/santasyncservice/SNTSyncState.m rename to Source/santasyncservice/SNTSyncState.mm diff --git a/Source/santasyncservice/SNTSyncTest.mm b/Source/santasyncservice/SNTSyncTest.mm index 1980d0a81..25f28ed53 100644 --- a/Source/santasyncservice/SNTSyncTest.mm +++ b/Source/santasyncservice/SNTSyncTest.mm @@ -856,12 +856,14 @@ - (void)testRuleDownload { state:SNTRuleStateBlock type:SNTRuleTypeCertificate customMsg:@"Hi There" - customURL:@"http://northpole.security"], + customURL:@"http://northpole.security" + celExpr:nil], [[SNTRule alloc] initWithIdentifier:@"AAAAAAAAAA" state:SNTRuleStateBlock type:SNTRuleTypeTeamID customMsg:@"Banned team ID" - customURL:@"http://northpole.security"], + customURL:@"http://northpole.security" + celExpr:nil], ]; OCMVerify([self.daemonConnRop databaseRuleAddRules:rules @@ -871,6 +873,47 @@ - (void)testRuleDownload { OCMVerify([self.daemonConnRop postRuleSyncNotificationForApplication:@"yes" reply:OCMOCK_ANY]); } +- (void)testRuleDownloadCel { + auto celEvaluator = santa::cel::Evaluator::Create(); + self.syncState.celEvaluator = celEvaluator->get(); + + SNTSyncRuleDownload *sut = [[SNTSyncRuleDownload alloc] initWithState:self.syncState]; + + NSData *respData = [self dataFromFixture:@"sync_ruledownload_with_cel_1.json"]; + [self stubRequestBody:respData + response:nil + error:nil + validateBlock:^BOOL(NSURLRequest *req) { + NSDictionary *requestDict = [self dictFromRequest:req]; + return requestDict[@"cursor"] == nil; + }]; + + // Stub out the call to invoke the block, verification of the input is later + OCMStub([self.daemonConnRop + databaseRuleAddRules:OCMOCK_ANY + ruleCleanup:SNTRuleCleanupNone + source:SNTRuleAddSourceSyncService + reply:([OCMArg invokeBlockWithArgs:[NSNull null], nil])]); + OCMStub([self.daemonConnRop postRuleSyncNotificationForApplication:[OCMArg any] + reply:([OCMArg invokeBlock])]); + [sut sync]; + + NSArray *rules = @[ + [[SNTRule alloc] + initWithIdentifier:@"AAAAAAAAAA" + state:SNTRuleStateCEL + type:SNTRuleTypeTeamID + customMsg:nil + customURL:nil + celExpr:@"target.signing_time >= timestamp('2025-05-31T00:00:00Z')"], + ]; + + OCMVerify([self.daemonConnRop databaseRuleAddRules:rules + ruleCleanup:SNTRuleCleanupNone + source:SNTRuleAddSourceSyncService + reply:OCMOCK_ANY]); +} + #pragma mark - SNTSyncPostflight Tests - (void)testPostflightBasicResponse { diff --git a/Source/santasyncservice/testdata/sync_ruledownload_with_cel_1.json b/Source/santasyncservice/testdata/sync_ruledownload_with_cel_1.json new file mode 100644 index 000000000..b3d9141f2 --- /dev/null +++ b/Source/santasyncservice/testdata/sync_ruledownload_with_cel_1.json @@ -0,0 +1,16 @@ +{ + "rules": [ + { + "rule_type": "TEAMID", + "policy": "CEL", + "identifier": "AAAAAAAAAA", + "cel_expr": "target.signing_time >= timestamp('2025-05-31T00:00:00Z')" + }, + { + "rule_type": "TEAMID", + "policy": "CEL", + "identifier": "BBBBBBBBBB", + "cel_expr": "this is an invalid expression" + } + ] +}