diff --git a/config/milo/activity/policies/backendtlspolicy-policy.yaml b/config/milo/activity/policies/backendtlspolicy-policy.yaml index 240e8996..e7fe5588 100644 --- a/config/milo/activity/policies/backendtlspolicy-policy.yaml +++ b/config/milo/activity/policies/backendtlspolicy-policy.yaml @@ -14,10 +14,13 @@ spec: kind: BackendTLSPolicy auditRules: - # BackendTLSPolicy creation with spec available + # BackendTLSPolicy creation with spec available. + # Rejected creates return a Status responseObject (no metadata.name); for + # generateName creates objectRef.name is also empty, so fall back to + # responseObject.details.name. has() is guarded per level. - name: create match: "!audit.user.username.startsWith('system:') && audit.verb == 'create' && has(audit.requestObject.spec)" - summary: "{{ actor }} created backend TLS policy {{ link(audit.responseObject.metadata.name, audit.objectRef) }}" + summary: "{{ actor }} created backend TLS policy {{ has(audit.responseObject.metadata.name) ? link(audit.responseObject.metadata.name, audit.objectRef) : has(audit.objectRef.name) ? link(audit.objectRef.name, audit.objectRef) : (has(audit.responseObject.details) && has(audit.responseObject.details.name)) ? link(audit.responseObject.details.name, audit.objectRef) : link('a backend TLS policy', audit.objectRef) }}" # BackendTLSPolicy creation fallback (no spec) - name: create-fallback diff --git a/config/milo/activity/policies/connector-policy.yaml b/config/milo/activity/policies/connector-policy.yaml index b8528eae..39f5a5bc 100644 --- a/config/milo/activity/policies/connector-policy.yaml +++ b/config/milo/activity/policies/connector-policy.yaml @@ -14,10 +14,15 @@ spec: kind: Connector auditRules: - # Connector creation with spec available + # Connector creation with spec available. + # Clients create Connectors via generateName (e.g. "datum-connect-"). A rejected + # create (quota/admission) returns a Status as responseObject with no metadata.name, + # and objectRef.name is empty because no name was assigned — so fall back to + # responseObject.details.name. has() is guarded per level (nested has errors on a + # missing intermediate). - name: create match: "!audit.user.username.startsWith('system:') && audit.verb == 'create' && has(audit.requestObject.spec)" - summary: "{{ actor }} created connector {{ link(audit.responseObject.metadata.name, audit.objectRef) }}" + summary: "{{ actor }} created connector {{ has(audit.responseObject.metadata.name) ? link(audit.responseObject.metadata.name, audit.objectRef) : has(audit.objectRef.name) ? link(audit.objectRef.name, audit.objectRef) : (has(audit.responseObject.details) && has(audit.responseObject.details.name)) ? link(audit.responseObject.details.name, audit.objectRef) : link('a connector', audit.objectRef) }}" # Connector creation fallback (no spec) - name: create-fallback diff --git a/config/milo/activity/policies/connectoradvertisement-policy.yaml b/config/milo/activity/policies/connectoradvertisement-policy.yaml index e7e40a77..1ee165e6 100644 --- a/config/milo/activity/policies/connectoradvertisement-policy.yaml +++ b/config/milo/activity/policies/connectoradvertisement-policy.yaml @@ -15,10 +15,13 @@ spec: kind: ConnectorAdvertisement auditRules: - # ConnectorAdvertisement creation with spec available + # ConnectorAdvertisement creation with spec available. + # Like Connector, advertisements may be created via generateName; a rejected create + # yields a Status responseObject (no metadata.name) with an empty objectRef.name, so + # fall back to responseObject.details.name. has() is guarded per level. - name: create match: "!audit.user.username.startsWith('system:') && audit.verb == 'create' && has(audit.requestObject.spec)" - summary: "{{ actor }} created connector advertisement {{ link(audit.responseObject.metadata.name, audit.objectRef) }}" + summary: "{{ actor }} created connector advertisement {{ has(audit.responseObject.metadata.name) ? link(audit.responseObject.metadata.name, audit.objectRef) : has(audit.objectRef.name) ? link(audit.objectRef.name, audit.objectRef) : (has(audit.responseObject.details) && has(audit.responseObject.details.name)) ? link(audit.responseObject.details.name, audit.objectRef) : link('a connector advertisement', audit.objectRef) }}" # ConnectorAdvertisement creation fallback (no spec) - name: create-fallback diff --git a/config/milo/activity/policies/domain-policy.yaml b/config/milo/activity/policies/domain-policy.yaml index b56e209f..b5f42043 100644 --- a/config/milo/activity/policies/domain-policy.yaml +++ b/config/milo/activity/policies/domain-policy.yaml @@ -15,10 +15,13 @@ spec: kind: Domain auditRules: - # Domain creation with spec.domainName available - use domain name as display text + # Domain creation with spec.domainName available - use domain name as display text. + # A rejected create returns a Status responseObject (no spec), so has() must guard + # the spec level too: nested has(responseObject.spec.domainName) errors when spec is + # absent. Fall back to objectRef.name, then responseObject.details.name (generateName). - name: create match: "!audit.user.username.startsWith('system:') && audit.verb == 'create' && has(audit.requestObject.spec)" - summary: "{{ actor }} created domain {{ link(audit.responseObject.spec.domainName, audit.objectRef) }}" + summary: "{{ actor }} created domain {{ (has(audit.responseObject.spec) && has(audit.responseObject.spec.domainName)) ? link(audit.responseObject.spec.domainName, audit.objectRef) : has(audit.objectRef.name) ? link(audit.objectRef.name, audit.objectRef) : (has(audit.responseObject.details) && has(audit.responseObject.details.name)) ? link(audit.responseObject.details.name, audit.objectRef) : link('a domain', audit.objectRef) }}" # Domain creation fallback (no spec) - name: create-fallback @@ -35,10 +38,11 @@ spec: match: "!audit.user.username.startsWith('system:') && audit.verb == 'delete'" summary: "{{ actor }} deleted a domain" - # Domain update with spec available - excludes status subresource + # Domain update with spec available - excludes status subresource. + # Same Status-response guarding as create: a rejected update has no responseObject.spec. - name: update match: "!audit.user.username.startsWith('system:') && audit.verb in ['update', 'patch'] && !has(audit.objectRef.subresource) && has(audit.requestObject.spec)" - summary: "{{ actor }} updated domain {{ link(audit.responseObject.spec.domainName, audit.objectRef) }}" + summary: "{{ actor }} updated domain {{ (has(audit.responseObject.spec) && has(audit.responseObject.spec.domainName)) ? link(audit.responseObject.spec.domainName, audit.objectRef) : has(audit.objectRef.name) ? link(audit.objectRef.name, audit.objectRef) : (has(audit.responseObject.details) && has(audit.responseObject.details.name)) ? link(audit.responseObject.details.name, audit.objectRef) : link('a domain', audit.objectRef) }}" # Domain update fallback (no spec) - name: update-fallback diff --git a/config/milo/activity/policies/gateway-policy.yaml b/config/milo/activity/policies/gateway-policy.yaml index 1cdcbb52..982cdbf7 100644 --- a/config/milo/activity/policies/gateway-policy.yaml +++ b/config/milo/activity/policies/gateway-policy.yaml @@ -15,9 +15,13 @@ spec: auditRules: # Gateway creation with spec available + # A rejected create returns a Status object as responseObject (no metadata.name). + # For generateName creates rejected before naming, objectRef.name is also empty, + # so fall back further to responseObject.details.name (carried on the Status). + # has() is guarded at each level: nested has(a.b.c) errors if b is absent. - name: create match: "!audit.user.username.startsWith('system:') && audit.verb == 'create' && has(audit.requestObject.spec)" - summary: "{{ actor }} created gateway {{ link(audit.responseObject.metadata.name, audit.objectRef) }}" + summary: "{{ actor }} created gateway {{ has(audit.responseObject.metadata.name) ? link(audit.responseObject.metadata.name, audit.objectRef) : has(audit.objectRef.name) ? link(audit.objectRef.name, audit.objectRef) : (has(audit.responseObject.details) && has(audit.responseObject.details.name)) ? link(audit.responseObject.details.name, audit.objectRef) : link('a gateway', audit.objectRef) }}" # Gateway creation fallback (no spec) - name: create-fallback diff --git a/config/milo/activity/policies/trafficprotectionpolicy-policy.yaml b/config/milo/activity/policies/trafficprotectionpolicy-policy.yaml index adb19e2e..6575e972 100644 --- a/config/milo/activity/policies/trafficprotectionpolicy-policy.yaml +++ b/config/milo/activity/policies/trafficprotectionpolicy-policy.yaml @@ -14,10 +14,13 @@ spec: kind: TrafficProtectionPolicy auditRules: - # TrafficProtectionPolicy creation with spec available + # TrafficProtectionPolicy creation with spec available. + # Rejected creates return a Status responseObject (no metadata.name); for + # generateName creates objectRef.name is also empty, so fall back to + # responseObject.details.name. has() is guarded per level. - name: create match: "!audit.user.username.startsWith('system:') && audit.verb == 'create' && has(audit.requestObject.spec)" - summary: "{{ actor }} created traffic protection policy {{ link(audit.responseObject.metadata.name, audit.objectRef) }}" + summary: "{{ actor }} created traffic protection policy {{ has(audit.responseObject.metadata.name) ? link(audit.responseObject.metadata.name, audit.objectRef) : has(audit.objectRef.name) ? link(audit.objectRef.name, audit.objectRef) : (has(audit.responseObject.details) && has(audit.responseObject.details.name)) ? link(audit.responseObject.details.name, audit.objectRef) : link('a traffic protection policy', audit.objectRef) }}" # TrafficProtectionPolicy creation fallback (no spec) - name: create-fallback