Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/northpolesec/santa-rule-importer/internal/morozconfig"
"github.com/northpolesec/santa-rule-importer/internal/rudolph"
"github.com/northpolesec/santa-rule-importer/internal/santactl"
"github.com/northpolesec/santa-rule-importer/internal/staticrules"
"github.com/northpolesec/santa-rule-importer/internal/zentral"

"google.golang.org/grpc"
Expand All @@ -25,9 +26,9 @@ import (
)

func usage() {
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] <path to config.toml|path to config.csv> <server>\n", os.Args[0])
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] <path to input file> <server>\n", os.Args[0])
fmt.Fprintln(os.Stderr)
fmt.Fprintf(os.Stderr, "santa-rule-importer - tool to import rules from Moroz, Rudolph, and Zentral to Workshop\n")
fmt.Fprintf(os.Stderr, "santa-rule-importer - tool to import rules from Moroz, Rudolph, Zentral, and StaticRules to Workshop\n")
fmt.Fprintln(os.Stderr)
fmt.Fprintf(os.Stderr, "This tool expects the Workshop API Key to be in the WORKSHOP_API_KEY env var\n")
fmt.Fprintf(os.Stderr, "For Zentral imports, set ZENTRAL_API_KEY env var with your Zentral API token\n")
Expand Down Expand Up @@ -101,8 +102,10 @@ func main() {
rules, ruleSrcErr = morozconfig.ParseRulesFromFile(filename, *useCustomMsgAsComment)
} else if strings.HasSuffix(filename, ".json") {
rules, ruleSrcErr = santactl.ParseRulesFromFile(filename)
} else if strings.HasSuffix(filename, ".mobileconfig") {
rules, ruleSrcErr = staticrules.ParseRulesFromFile(filename)
} else {
println("Unsupported file format. Please provide a .toml, .csv, or .json file.")
println("Unsupported file format. Please provide a .toml, .csv, .json, or .mobileconfig file.")
os.Exit(1)
}
}
Expand All @@ -111,7 +114,7 @@ func main() {
if *zentBaseURL != "" {
log.Fatalf("Failed to retrieve rules from Zentral: %v", ruleSrcErr)
} else {
log.Fatalf("Failed to read config file: %v", ruleSrcErr)
log.Fatalf("Failed to read input file: %v", ruleSrcErr)
}
}

Expand All @@ -138,6 +141,8 @@ func main() {

// Iterate over the rules and add them to the Workshop instance
for i, rule := range rules {
// TODO: Support setting the tag
rule.SetTag("global")
req.Rule = rule
_, err := client.CreateRule(context.Background(), req)

Expand Down
11 changes: 6 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ module github.com/northpolesec/santa-rule-importer
go 1.24

require (
buf.build/gen/go/northpolesec/protos/protocolbuffers/go v1.36.5-20250214013108-459ace571a4b.1
buf.build/gen/go/northpolesec/workshop-api/grpc/go v1.5.1-20250310185908-3540211763ba.2
buf.build/gen/go/northpolesec/workshop-api/protocolbuffers/go v1.36.5-20250310185908-3540211763ba.1
buf.build/gen/go/northpolesec/protos/protocolbuffers/go v1.36.11-20251222234036-85229a4bd2f9.1
buf.build/gen/go/northpolesec/workshop-api/grpc/go v1.6.0-20260107160335-e638de704eb2.1
buf.build/gen/go/northpolesec/workshop-api/protocolbuffers/go v1.36.11-20260107160335-e638de704eb2.1
github.com/pelletier/go-toml/v2 v2.2.3
github.com/shoenig/test v1.12.1
google.golang.org/grpc v1.71.0
howett.net/plist v1.0.1
)

require (
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/protobuf v1.36.5 // indirect
google.golang.org/protobuf v1.36.11 // indirect
)
25 changes: 15 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
buf.build/gen/go/northpolesec/protos/protocolbuffers/go v1.36.5-20250214013108-459ace571a4b.1 h1:AjsbB80GYWKwVG7GK+VL0vni5tYBfZyj3tdOw5YeuV4=
buf.build/gen/go/northpolesec/protos/protocolbuffers/go v1.36.5-20250214013108-459ace571a4b.1/go.mod h1:nhgtguJb5Wg2nzlWrhd2MCZqayMQ2gvUKNVV0U0YQHU=
buf.build/gen/go/northpolesec/workshop-api/grpc/go v1.5.1-20250310185908-3540211763ba.2 h1:vmqKuYke66++lMdczfvawMZ8N3qO/Qda3oDdjZG9MJA=
buf.build/gen/go/northpolesec/workshop-api/grpc/go v1.5.1-20250310185908-3540211763ba.2/go.mod h1:sWHk0DJknB0IBrnA7fUcUFbGI1gfPVgDS11S7RkYaBU=
buf.build/gen/go/northpolesec/workshop-api/protocolbuffers/go v1.36.5-20250310185908-3540211763ba.1 h1:abvT5xheHLfdjE+9bSfKoJYg5VNizI6Hk/TEhaGdRxI=
buf.build/gen/go/northpolesec/workshop-api/protocolbuffers/go v1.36.5-20250310185908-3540211763ba.1/go.mod h1:CfwEab2SbVCVCTXhKFGplB8iHHn4cgPjEfjIz9IDf2g=
buf.build/gen/go/northpolesec/protos/protocolbuffers/go v1.36.11-20251222234036-85229a4bd2f9.1 h1:CMLPSLdebEOauD8iaH04+vGRxW7GaX3uPaisdRr8OeM=
buf.build/gen/go/northpolesec/protos/protocolbuffers/go v1.36.11-20251222234036-85229a4bd2f9.1/go.mod h1:OfafKwDkrVDn7wb5tOCm86GmO5v23MErKzv+H9ZcOts=
buf.build/gen/go/northpolesec/workshop-api/grpc/go v1.6.0-20260107160335-e638de704eb2.1 h1:YYtX6akxNB+uolze5oPV4bIC/X9ol6mlDq62jrzIwD4=
buf.build/gen/go/northpolesec/workshop-api/grpc/go v1.6.0-20260107160335-e638de704eb2.1/go.mod h1:o9xFpidrX9l88/2bFiq81IaeL2+rwPsust+VHPBjkms=
buf.build/gen/go/northpolesec/workshop-api/protocolbuffers/go v1.36.11-20260107160335-e638de704eb2.1 h1:laOWZwGARNuCtTO50avfJ2i5lnibaK9BSryMKCWRDBs=
buf.build/gen/go/northpolesec/workshop-api/protocolbuffers/go v1.36.11-20260107160335-e638de704eb2.1/go.mod h1:CklHzZhiLzptCaI6Xa0VSHdXG5InjT3A6fDrCKpOS1c=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
Expand All @@ -12,10 +12,11 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down Expand Up @@ -48,7 +49,11 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
9 changes: 4 additions & 5 deletions internal/morozconfig/morozconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/shoenig/test"
"github.com/shoenig/test/must"

syncpb "buf.build/gen/go/northpolesec/protos/protocolbuffers/go/sync"
apipb "buf.build/gen/go/northpolesec/workshop-api/protocolbuffers/go/workshop/v1"
)

Expand All @@ -26,15 +25,15 @@ func TestParseRulesFromFile(t *testing.T) {

must.Eq(t, 2, len(rules))

test.Eq(t, syncpb.Policy_BLOCKLIST, rules[0].GetPolicy())
test.Eq(t, syncpb.RuleType_SIGNINGID, rules[0].GetRuleType())
test.Eq(t, apipb.Policy_BLOCKLIST, rules[0].GetPolicy())
test.Eq(t, apipb.RuleType_SIGNINGID, rules[0].GetRuleType())
test.Eq(t, "platform:com.apple.osacompile", rules[0].GetIdentifier())
test.Eq(t, "https://gist.github.com/pmarkowsky/bfa5840261351f506444b2d8541e9654",
rules[0].GetCustomUrl())
test.Eq(t, "osacompile is banned by policy", rules[0].GetCustomMsg())

test.Eq(t, syncpb.Policy_BLOCKLIST, rules[1].GetPolicy())
test.Eq(t, syncpb.RuleType_SIGNINGID, rules[1].GetRuleType())
test.Eq(t, apipb.Policy_BLOCKLIST, rules[1].GetPolicy())
test.Eq(t, apipb.RuleType_SIGNINGID, rules[1].GetRuleType())
test.Eq(t, "platform:com.apple.osascript", rules[1].GetIdentifier())
test.Eq(t, "https://www.youtube.com/watch?v=dQw4w9WgXcQ", rules[1].GetCustomUrl())
test.Eq(t, "Where does this go?", rules[1].GetCustomMsg())
Expand Down
28 changes: 14 additions & 14 deletions internal/rulehelpers/rulehelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,40 @@ import (
"log"
"strings"

syncpb "buf.build/gen/go/northpolesec/protos/protocolbuffers/go/sync"
apipb "buf.build/gen/go/northpolesec/workshop-api/protocolbuffers/go/workshop/v1"
)

// GetPolicyType maps a string to a syncpb.Policy type or panics if it's unknown.
func GetPolicyType(policy string) syncpb.Policy {
// GetPolicyType maps a string to an apipb.Policy type or panics if it's unknown.
func GetPolicyType(policy string) apipb.Policy {
policy = strings.ToUpper(policy)
switch policy {
case "ALLOWLIST", "ALLOW":
return syncpb.Policy_ALLOWLIST
return apipb.Policy_ALLOWLIST
case "BLOCK", "BLOCKLIST":
return syncpb.Policy_BLOCKLIST
return apipb.Policy_BLOCKLIST
default:
log.Fatalf("Unknown policy type: %s", policy)
return syncpb.Policy_POLICY_UNKNOWN
return apipb.Policy_POLICY_UNKNOWN
}
}

// GetRuleType maps a string to a syncpb.RuleType type or panics if it's unknown.
func GetRuleType(ruleType string) syncpb.RuleType {
// GetRuleType maps a string to an apipb.RuleType type or panics if it's unknown.
func GetRuleType(ruleType string) apipb.RuleType {
ruleType = strings.ToUpper(ruleType)
switch ruleType {
case "CDHASH":
return syncpb.RuleType_CDHASH
return apipb.RuleType_CDHASH
case "SHA256", "BINARY":
return syncpb.RuleType_BINARY
return apipb.RuleType_BINARY
case "SIGNINGID":
return syncpb.RuleType_SIGNINGID
return apipb.RuleType_SIGNINGID
case "CERTIFICATE":
return syncpb.RuleType_CERTIFICATE
return apipb.RuleType_CERTIFICATE
case "TEAMID":
return syncpb.RuleType_TEAMID
return apipb.RuleType_TEAMID
default:
log.Fatalf("Unknown rule type: %s", ruleType)
// Should never reach here, but return BINARY as a fallback
return syncpb.RuleType_BINARY
return apipb.RuleType_BINARY
}
}
14 changes: 7 additions & 7 deletions internal/santactl/santactl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/shoenig/test"
"github.com/shoenig/test/must"

syncpb "buf.build/gen/go/northpolesec/protos/protocolbuffers/go/sync"
apipb "buf.build/gen/go/northpolesec/workshop-api/protocolbuffers/go/workshop/v1"
)

func TestRuleTranslation(t *testing.T) {
Expand All @@ -19,23 +19,23 @@ func TestRuleTranslation(t *testing.T) {
must.Eq(t, 3, len(rules))

test.Eq(t, "EQHXZ8M8AV:com.google.Chrome.helper.renderer", rules[0].GetIdentifier())
test.Eq(t, syncpb.RuleType_SIGNINGID, rules[0].GetRuleType())
test.Eq(t, syncpb.Policy_ALLOWLIST, rules[0].GetPolicy())
test.Eq(t, apipb.RuleType_SIGNINGID, rules[0].GetRuleType())
test.Eq(t, apipb.Policy_ALLOWLIST, rules[0].GetPolicy())
test.Eq(t, "This is allowed", rules[0].GetCustomMsg())
test.Eq(t, "https://support.google.com/chrome/answer/95617",
rules[0].GetCustomUrl())
test.Eq(t, "/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/131.0.6778.86/Helpers/Google Chrome Helper (Renderer).app/Contents/MacOS/Google Chrome Helper (Renderer)", rules[0].GetComment())

test.Eq(t, "6c58905785bccb8a0854cca5a646c4ea6b20e522c9b61de842a759919df002e7", rules[1].GetIdentifier())
test.Eq(t, syncpb.RuleType_BINARY, rules[1].GetRuleType())
test.Eq(t, syncpb.Policy_ALLOWLIST, rules[1].GetPolicy())
test.Eq(t, apipb.RuleType_BINARY, rules[1].GetRuleType())
test.Eq(t, apipb.Policy_ALLOWLIST, rules[1].GetPolicy())
test.Eq(t, "", rules[1].GetCustomMsg())
test.Eq(t, "", rules[1].GetCustomUrl())
test.Eq(t, "/Users/peterm/Library/Application Support/Code/User/globalStorage/llvm-vs-code-extensions.vscode-clangd/install/15.0.6/clangd_15.0.6/bin/clangd", rules[1].GetComment())

test.Eq(t, "platform:com.apple.osascript", rules[2].GetIdentifier())
test.Eq(t, syncpb.RuleType_SIGNINGID, rules[2].GetRuleType())
test.Eq(t, syncpb.Policy_BLOCKLIST, rules[2].GetPolicy())
test.Eq(t, apipb.RuleType_SIGNINGID, rules[2].GetRuleType())
test.Eq(t, apipb.Policy_BLOCKLIST, rules[2].GetPolicy())
test.Eq(t, "", rules[2].GetCustomMsg())
test.Eq(t, "", rules[2].GetCustomUrl())
test.Eq(t, "", rules[2].GetComment())
Expand Down
4 changes: 4 additions & 0 deletions internal/staticrules/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Package staticrules provides code for extracting rules from
// Santa mobileconfig profiles that contain StaticRules configuration.
// Reference: https://northpole.dev/deployment/profile-configuration/
package staticrules
72 changes: 72 additions & 0 deletions internal/staticrules/staticrules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package staticrules

import (
"fmt"
"os"

"github.com/northpolesec/santa-rule-importer/internal/rulehelpers"
"howett.net/plist"

apipb "buf.build/gen/go/northpolesec/workshop-api/protocolbuffers/go/workshop/v1"
)

// Rule represents a single rule from the StaticRules array in a mobileconfig file.
type Rule struct {
Identifier string `plist:"identifier"`
Policy string `plist:"policy"`
RuleType string `plist:"rule_type"`
CustomMsg string `plist:"custom_msg"`
CustomURL string `plist:"custom_url"`
CelExpr string `plist:"cel_expr"`
Comment string `plist:"comment"`
}

// PayloadContent represents the Santa configuration payload within a mobileconfig.
type PayloadContent struct {
StaticRules []Rule `plist:"StaticRules"`
}

// MobileConfig represents the structure of a Santa mobileconfig file.
type MobileConfig struct {
PayloadContent []PayloadContent `plist:"PayloadContent"`
}

// ParseRulesFromFile reads a mobileconfig file and returns the rules from the
// StaticRules array converted to Workshop API format.
func ParseRulesFromFile(filePath string) ([]*apipb.Rule, error) {
f, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer f.Close()

var config MobileConfig
decoder := plist.NewDecoder(f)
if err := decoder.Decode(&config); err != nil {
return nil, fmt.Errorf("failed to decode plist: %w", err)
}

if len(config.PayloadContent) == 0 {
return nil, fmt.Errorf("no PayloadContent found in mobileconfig")
}

staticRules := config.PayloadContent[0].StaticRules
if len(staticRules) == 0 {
return []*apipb.Rule{}, nil
}

rules := make([]*apipb.Rule, len(staticRules))
for i, rule := range staticRules {
rules[i] = &apipb.Rule{
RuleType: rulehelpers.GetRuleType(rule.RuleType),
Policy: rulehelpers.GetPolicyType(rule.Policy),
Identifier: rule.Identifier,
CustomMsg: rule.CustomMsg,
CustomUrl: rule.CustomURL,
CelExpr: rule.CelExpr,
Comment: rule.Comment,
}
}

return rules, nil
}
45 changes: 45 additions & 0 deletions internal/staticrules/staticrules_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package staticrules_test

import (
"testing"

"github.com/northpolesec/santa-rule-importer/internal/staticrules"
"github.com/shoenig/test"
"github.com/shoenig/test/must"

apipb "buf.build/gen/go/northpolesec/workshop-api/protocolbuffers/go/workshop/v1"
)

func TestParseRulesFromFile(t *testing.T) {
rules, err := staticrules.ParseRulesFromFile("testdata/santa.mobileconfig")
must.NoError(t, err)

must.Eq(t, 3, len(rules))

// First rule: TEAMID allowlist
test.Eq(t, "ZMCG7MLDV9", rules[0].GetIdentifier())
test.Eq(t, apipb.RuleType_TEAMID, rules[0].GetRuleType())
test.Eq(t, apipb.Policy_ALLOWLIST, rules[0].GetPolicy())
test.Eq(t, "North Pole Security Inc", rules[0].GetComment())
test.Eq(t, "", rules[0].GetCustomMsg())
test.Eq(t, "", rules[0].GetCustomUrl())
test.Eq(t, "", rules[0].GetCelExpr())

// Second rule: BINARY blocklist with custom_msg and custom_url
test.Eq(t, "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2", rules[1].GetIdentifier())
test.Eq(t, apipb.RuleType_BINARY, rules[1].GetRuleType())
test.Eq(t, apipb.Policy_BLOCKLIST, rules[1].GetPolicy())
test.Eq(t, "This binary is not allowed", rules[1].GetCustomMsg())
test.Eq(t, "https://example.com/help", rules[1].GetCustomUrl())
test.Eq(t, "", rules[1].GetComment())
test.Eq(t, "", rules[1].GetCelExpr())

// Third rule: SIGNINGID allowlist with cel_expr
test.Eq(t, "EQHXZ8M8AV:com.google.Chrome", rules[2].GetIdentifier())
test.Eq(t, apipb.RuleType_SIGNINGID, rules[2].GetRuleType())
test.Eq(t, apipb.Policy_ALLOWLIST, rules[2].GetPolicy())
test.Eq(t, `target.signing_time >= timestamp('2025-05-31T00:00:00Z')`, rules[2].GetCelExpr())
test.Eq(t, "", rules[2].GetCustomMsg())
test.Eq(t, "", rules[2].GetCustomUrl())
test.Eq(t, "", rules[2].GetComment())
}
Loading
Loading