aws-quicksight-datasource-exposes-credential

A QuickSight data source that stores database credentials (CredentialPair / API key) exposes those credentials - readable by the QuickSight service and potentially exfiltrable by updating the data source endpoint.

derived aws emits ExposesCredential

match

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

{'datasource': None} ContainsSecret {'secret': None}

where

node_type(?datasource) == GenericData ?datasource.provider_type == 'AWS::QuickSight::DataSource' ?datasource.credential_type in ['USERNAME_PASSWORD', 'API_KEY', 'CREDENTIAL_PAIR']

emit

source typeData
target typeCredential
source?datasource
target?secret
conditionsservice_state
state logicCONDITIONAL(service_state) when the data source status cannot be confirmed as CREATION_SUCCESSFUL (status is absent/uncollected/UNKNOWN). ACTIVE only when the data source is explicitly in CREATION_SUCCESSFUL state and configured with a non-AWS-IAM credential type (CredentialPair / API key). BLOCKED when credential type is IAM (no stored credential; QuickSight uses the service role instead).

Narrative

QuickSight data source {datasource.name} stores {secret.credential_type} credentials for its backend. These credentials are held internally by QuickSight and used to authenticate to the data source on every query and refresh.

Raw rule rules/derived/aws/quicksight.yaml

id: aws-quicksight-datasource-exposes-credential
emits: ExposesCredential
description: "A QuickSight data source that stores database credentials (CredentialPair / API key) exposes\
  \ those credentials \u2014 readable by the QuickSight service and potentially exfiltrable by updating\
  \ the data source endpoint."
match:
- - datasource: null
  - ContainsSecret
  - secret: null
where:
- node_type(?datasource) == GenericData
- ?datasource.provider_type == 'AWS::QuickSight::DataSource'
- ?datasource.credential_type in ['USERNAME_PASSWORD', 'API_KEY', 'CREDENTIAL_PAIR']
emit:
  source_type: Data
  target_type: Credential
  source: ?datasource
  target: ?secret
  permissions: []
  conditions:
  - service_state
  state_logic: CONDITIONAL(service_state) when the data source status cannot be confirmed as CREATION_SUCCESSFUL
    (status is absent/uncollected/UNKNOWN). ACTIVE only when the data source is explicitly in CREATION_SUCCESSFUL
    state and configured with a non-AWS-IAM credential type (CredentialPair / API key). BLOCKED when credential
    type is IAM (no stored credential; QuickSight uses the service role instead).
  confidence: min(contributing_confidences) * 0.85
  derived_from:
  - aws-quicksight-datasource-contains-secret
  false_positive_note: "QuickSight does NOT expose plaintext credentials via DescribeDataSource API \u2014\
    \ the credential fields are redacted. This edge expresses that the credentials ARE stored and used\
    \ by the service (they can be exfiltrated indirectly via UpdateDataSource endpoint replacement \u2014\
    \ see rule aws-quicksight-ssrf-datasource-cred). Do NOT emit for IAM-credential data sources (S3,\
    \ Athena, Redshift via IAM) \u2014 those use the QuickSight service role, not stored credentials;\
    \ the role's access is modeled via CanReadData. credential-chains.yaml read-secret-yields-identity\
    \ can only fire when CredentialsFor(secret -> DB identity) is also emitted by the explicit normalization."
  narrative: QuickSight data source {datasource.name} stores {secret.credential_type} credentials for
    its backend. These credentials are held internally by QuickSight and used to authenticate to the data
    source on every query and refresh.
move · open · esc close