messaging-trigger-executes-as
Triggering an event-driven consumer that executes as an identity runs the attacker-influenced payload as that identity (generic messaging case; CI/CD runners are cicd-chains').
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_class(?consumer) == Compute or node_type(?consumer) ==
Workflow
?consumer is an event-driven consumer, not a CI/CD build/deploy runner (else defer to cicd-chains)
emit
| source type | Identity |
|---|---|
| target type | Identity |
| source | ?principal |
| target | ?identity |
| conditions | trigger_exists |
| state logic | CONDITIONAL(trigger_exists) by default — the trigger delivers the attacker payload to the consumer only when a LIVE event-source binding routes it and the consumer processes attacker-controllable input (the common messaging case: the consumer parses the message body/attributes the attacker sends). ACTIVE when the binding is confirmed live and the consumer runs on the delivered payload without further gating. Never stronger than the contributing CanTrigger (rule 1's state flows through) or ExecutesAs. A BLOCKED/deny-gated trigger blocks this. |
Narrative
{principal.name} can trigger {consumer.name} (via a message/event it publishes), and {consumer.name} executes as {identity.name}; the delivered attacker-influenced payload therefore runs as {identity.name}.
Raw rule rules/derived/messaging-chains.yaml
id: messaging-trigger-executes-as
emits: CanExecuteAs
description: Triggering an event-driven consumer that executes as an identity runs the attacker-influenced
payload as that identity (generic messaging case; CI/CD runners are cicd-chains').
applies_to:
- '*'
match:
- - principal: null
- CanTrigger
- consumer: null
- - consumer: null
- ExecutesAs
- identity: null
where:
- node_class(?consumer) == Compute or node_type(?consumer) == Workflow
- ?consumer is an event-driven consumer, not a CI/CD build/deploy runner (else defer to cicd-chains)
emit:
source_type: Identity
target_type: Identity
source: ?principal
target: ?identity
permissions: []
conditions:
- trigger_exists
state_logic: "CONDITIONAL(trigger_exists) by default \u2014 the trigger delivers the attacker payload\
\ to the consumer only when a LIVE event-source binding routes it and the consumer processes attacker-controllable\
\ input (the common messaging case: the consumer parses the message body/attributes the attacker sends).\
\ ACTIVE when the binding is confirmed live and the consumer runs on the delivered payload without\
\ further gating. Never stronger than the contributing CanTrigger (rule 1's state flows through) or\
\ ExecutesAs. A BLOCKED/deny-gated trigger blocks this."
confidence: min(contributing_confidences) * 0.9
derived_from:
- ?principal CanTrigger ?consumer
- ?consumer ExecutesAs ?identity
false_positive_note: "CITE, DO NOT DUPLICATE cicd-chains: when ?consumer is a CI/CD build/deploy runner,\
\ cicd-trigger-runner-executes-as owns this collapse (with its attacker_influenced_definition gate)\
\ \u2014 this rule handles the GENERIC event-driven consumer and dedupes on (CanExecuteAs, ?principal,\
\ ?identity). Kept CONDITIONAL because a trigger is not code control: it yields the identity only\
\ if the consumer actually runs attacker-influenced input from the delivered event (nearly always\
\ true for a message-body-parsing consumer, but a consumer that ignores the payload and only reacts\
\ to the fact-of-event is weaker). The escalation consequence (identity outranks the principal) is\
\ produced by can-execute-as / can-control off this CanExecuteAs \u2014 cite, do not re-derive. A\
\ consumer that requires modifying its CODE to run attacker logic is the execute-as-via-code-modify\
\ case, not this rule."
narrative: '{principal.name} can trigger {consumer.name} (via a message/event it publishes), and {consumer.name}
executes as {identity.name}; the delivered attacker-influenced payload therefore runs as {identity.name}.'