aws-cloudwatch-read-log-data

logs:GetLogEvents, logs:FilterLogEvents, or logs:StartQuery + logs:GetQueryResults on a log group enables reading raw log events from the LoggingService node; application logs frequently embed secrets, tokens, or credentials usable for credential harvest.

derived aws emits CanRead

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} HasPermission {'log_group': None}

where

node_type(?log_group) == LoggingService ?log_group.provider_type == 'AWS::Logs::LogGroup' ?principal has EFFECTIVE (logs:GetLogEvents OR logs:FilterLogEvents OR (logs:StartQuery AND logs:GetQueryResults)) on ?log_group ARN

emit

source typeIdentity
target typeLoggingService
source?principal
target?log_group
permissionslogs:GetLogEvents logs:FilterLogEvents logs:StartQuery logs:GetQueryResults
conditionsiam_permission resource_policy key_permission
state logicACTIVE when logs:GetLogEvents OR logs:FilterLogEvents is confirmed EFFECTIVE on the log group ARN (identity-based policy + no resource-policy deny + no SCP deny) AND the log group is NOT KMS-encrypted (or the caller also holds kms:Decrypt on the log group's CMK). ACTIVE when both logs:StartQuery AND logs:GetQueryResults are confirmed EFFECTIVE on the log group ARN (Insights query pair; neither action alone yields data). CONDITIONAL(iam_permission) when only one of the Insights pair is confirmed (StartQuery without GetQueryResults or vice versa). CONDITIONAL(key_permission) when the log group has a kmsKeyId set and kms:Decrypt on that CMK is not confirmed — credential-chains:decrypt-gates-secret-read upgrades to ACTIVE when CanDecrypt is confirmed on the CMK. CONDITIONAL(resource_policy) when a resource policy deny or missing grant caps the effective permission. BLOCKED by an explicit deny on logs:GetLogEvents / logs:FilterLogEvents / logs:StartQuery / logs:GetQueryResults, or on kms:Decrypt (for encrypted groups). The downstream ContainsSecret + CanImpersonate chain (credential-chains) is CONDITIONAL on the ContainsSecret annotation being confirmed — a heuristic ContainsSecret (confidence 0.5) does not produce an ACTIVE CanImpersonate.

Narrative

{principal.name} can read log events from {log_group.name} (logs:GetLogEvents / logs:FilterLogEvents / logs:StartQuery + logs:GetQueryResults). Application log groups frequently contain credential material embedded in log output (environment variable dumps, stack traces exposing connection strings, SDK debug logging printing session tokens). If {log_group.name} carries a ContainsSecret annotation, the credential-chains rollup (read-secret-yields- identity) produces CanImpersonate over the embedded credential's target identity. This is a credential-harvest path - CanRead on a log group does not by itself grant access to compute or IAM.

Raw rule rules/derived/aws/cloudwatch.yaml

id: aws-cloudwatch-read-log-data
emits: CanRead
description: logs:GetLogEvents, logs:FilterLogEvents, or logs:StartQuery + logs:GetQueryResults on a log
  group enables reading raw log events from the LoggingService node; application logs frequently embed
  secrets, tokens, or credentials usable for credential harvest.
match:
- - principal: null
  - HasPermission
  - log_group: null
where:
- node_type(?log_group) == LoggingService
- ?log_group.provider_type == 'AWS::Logs::LogGroup'
- ?principal has EFFECTIVE (logs:GetLogEvents OR logs:FilterLogEvents OR (logs:StartQuery AND logs:GetQueryResults))
  on ?log_group ARN
emit:
  source_type: Identity
  target_type: LoggingService
  source: ?principal
  target: ?log_group
  permissions:
  - logs:GetLogEvents
  - logs:FilterLogEvents
  - logs:StartQuery
  - logs:GetQueryResults
  conditions:
  - iam_permission
  - resource_policy
  - key_permission
  state_logic: "ACTIVE when logs:GetLogEvents OR logs:FilterLogEvents is confirmed EFFECTIVE on the log\
    \ group ARN (identity-based policy + no resource-policy deny + no SCP deny) AND the log group is NOT\
    \ KMS-encrypted (or the caller also holds kms:Decrypt on the log group's CMK). ACTIVE when both logs:StartQuery\
    \ AND logs:GetQueryResults are confirmed EFFECTIVE on the log group ARN (Insights query pair; neither\
    \ action alone yields data). CONDITIONAL(iam_permission) when only one of the Insights pair is confirmed\
    \ (StartQuery without GetQueryResults or vice versa). CONDITIONAL(key_permission) when the log group\
    \ has a kmsKeyId set and kms:Decrypt on that CMK is not confirmed \u2014 credential-chains:decrypt-gates-secret-read\
    \ upgrades to ACTIVE when CanDecrypt is confirmed on the CMK. CONDITIONAL(resource_policy) when a\
    \ resource policy deny or missing grant caps the effective permission. BLOCKED by an explicit deny\
    \ on logs:GetLogEvents / logs:FilterLogEvents / logs:StartQuery / logs:GetQueryResults, or on kms:Decrypt\
    \ (for encrypted groups). The downstream ContainsSecret + CanImpersonate chain (credential-chains)\
    \ is CONDITIONAL on the ContainsSecret annotation being confirmed \u2014 a heuristic ContainsSecret\
    \ (confidence 0.5) does not produce an ACTIVE CanImpersonate."
  confidence: min(contributing_confidences) * 0.9
  derived_from:
  - ?principal HasPermission ?log_group (logs:GetLogEvents / logs:FilterLogEvents effective permission)
  - "credential-chains:read-secret-yields-identity (downstream credential harvest \u2014 cited not re-derived)"
  - "credential-chains:decrypt-gates-secret-read (KMS-encrypted log group gate \u2014 cited not re-derived)"
  false_positive_note: "logs:GetLogEvents and logs:FilterLogEvents are distinct actions; either alone\
    \ is sufficient for log-event access (no pair requirement). logs:GetLogEvents reads a specific log\
    \ stream; logs:FilterLogEvents searches across all streams in the group with a filter pattern. Either\
    \ grants the CanRead capability \u2014 emit for whichever is confirmed effective. logs:StartQuery\
    \ and logs:GetQueryResults form an Insights query pair: BOTH actions are required on the log group\
    \ ARN to yield results (StartQuery initiates the query, GetQueryResults retrieves results). Emit ACTIVE\
    \ only when BOTH are confirmed; emit CONDITIONAL(iam_permission) if only one is confirmed. KMS-encrypted\
    \ log groups: logs:GetLogEvents returns encrypted event data when called by a principal that lacks\
    \ kms:Decrypt on the log group's CMK \u2014 emit as CONDITIONAL(key_permission) in this case. credential-chains:decrypt-gates-\
    \ secret-read handles the upgrade when CanDecrypt is confirmed. ContainsSecret is a heuristic annotation\
    \ \u2014 not all log groups contain credential material. The CanRead edge is always valid when the\
    \ permission is confirmed; the downstream CanImpersonate (credential-chains) is only ACTIVE when ContainsSecret\
    \ is confirmed with high confidence and CredentialsFor an identity is evidenced. Do not over-claim:\
    \ a log group of VPC Flow Logs contains IP addresses, not credentials \u2014 CanRead is still valid\
    \ for recon but ContainsSecret should NOT be annotated. logs:DescribeLogGroups / logs:DescribeLogStreams\
    \ are recon-grade and do NOT warrant this CanRead edge (they return metadata, not event content).\
    \ Emits CanRead (not CanReadData) because schema/edges.yaml CanReadData.target is restricted to [Storage,\
    \ Data, Messaging]; LoggingService is a ManagementService subtype and falls outside that set."
  narrative: "{principal.name} can read log events from {log_group.name} (logs:GetLogEvents / logs:FilterLogEvents\
    \ / logs:StartQuery + logs:GetQueryResults). Application log groups frequently contain credential\
    \ material embedded in log output (environment variable dumps, stack traces exposing connection strings,\
    \ SDK debug logging printing session tokens). If {log_group.name} carries a ContainsSecret annotation,\
    \ the credential-chains rollup (read-secret-yields- identity) produces CanImpersonate over the embedded\
    \ credential's target identity. This is a credential-harvest path \u2014 CanRead on a log group does\
    \ not by itself grant access to compute or IAM."
move · open · esc close