This repository builds a CCF RunnerV2 plugin for AWS Secrets Manager. It collects read-only Secrets Manager metadata, resource policies, version stages, tags, and CloudTrail event history, then evaluates configured Rego policy bundles against one normalized record per secret ARN.
The plugin never calls GetSecretValue.
The plugin registers one subject template:
| Name | Type | Identity labels |
|---|---|---|
aws-secretsmanager-secret |
component | account_id, region, resource_id |
Every evidence record also includes labels: provider=aws, type=secretsmanager, subject=aws-secretsmanager-secret, account_id, region, resource_id, resource_arn, resource_type=secret, and account_tag_<key> for account config tags. resource_id is the ARN segment after the final : and keeps the AWS 6-character suffix.
The CCF agent passes flat string config. Structured values are JSON strings.
| Key | Default | Notes |
|---|---|---|
accounts |
[] |
JSON array of {account_id, regions[], role_arn, external_id, session_name, tags{}}. Empty uses the ambient AWS credential chain. |
default_regions |
[] |
JSON array used when an account omits regions; then falls back to the SDK default region. |
lookback_days |
90 |
Positive integer up to 90, used for CloudTrail event history. |
policy_inputs |
{} |
JSON object exposed to Rego as input.policy_inputs. |
policy_input |
{} |
Alias for policy_inputs. |
policy_labels |
{} |
JSON string map merged into evidence labels. |
max_concurrency |
4 |
Positive worker count for account/region targets; 0 is normalized to 1. |
api_timeout_seconds |
120 |
Positive per-target budget covering all paginated Secrets Manager, CloudTrail, and STS calls for one account/region. |
Runtime logs default to info. Set LOG_LEVEL to debug, info, warn, or error to override the level.
Each secret is marshaled with resource.type = "secret":
{
"schema_version": "v1",
"source": "aws-secretsmanager",
"account": {"account_id": "123456789012", "role_arn": "", "tags": {"environment": "prod"}},
"region": {"name": "us-east-1"},
"resource": {
"id": "MyApp/db/credentials-AbCdEf",
"arn": "arn:aws:secretsmanager:us-east-1:123456789012:secret:MyApp/db/credentials-AbCdEf",
"type": "secret"
},
"config": {
"secret_arn": "arn:aws:secretsmanager:us-east-1:123456789012:secret:MyApp/db/credentials-AbCdEf",
"name_hash": "sha256:...",
"kms_key_id": "aws/secretsmanager",
"rotation_enabled": true,
"rotation_lambda_arn": "arn:aws:lambda:us-east-1:123456789012:function:rotate-db",
"rotation_rules": {"automatically_after_days": 30, "schedule_expression": "", "duration": ""},
"last_rotated_date": "2026-04-12T03:00:00Z",
"last_changed_date": "2026-04-12T03:00:00Z",
"last_accessed_date": "2026-05-26T14:22:11Z",
"deleted_date": "",
"recovery_window_days": 0,
"owning_service": "",
"replication_status": [
{"region": "us-west-2", "status": "InSync", "last_accessed_date": "2026-05-26T14:22:11Z", "status_message": ""}
],
"description_hash": "sha256:...",
"resource_policy": {
"hash": "sha256:...",
"document": {"Version": "2012-10-17", "Statement": []},
"principals": [
{"principal": "arn:aws:iam::123456789012:role/app-reader", "action": ["secretsmanager:GetSecretValue"], "condition": null, "effect": "Allow"}
]
},
"resource_policy_present": true,
"versions": [
{"version_id": "abc-123", "created_date": "2026-04-12T03:00:00Z", "kms_key_ids": ["arn:aws:kms:us-east-1:123456789012:key/..."], "stages": ["AWSCURRENT"]},
{"version_id": "def-456", "created_date": "2026-03-13T03:00:00Z", "kms_key_ids": ["arn:aws:kms:us-east-1:123456789012:key/..."], "stages": ["AWSPREVIOUS"]}
],
"deprecated_version_count": 0
},
"dynamic": {
"cloudtrail_events": [
{"event_name": "RotateSecret", "event_time": "2026-04-12T03:00:00Z", "user_identity_arn": "arn:aws:iam::123456789012:role/rotation", "aws_region": "us-east-1", "event_id": "evt-1", "resources": ["MyApp/db/credentials-AbCdEf"]}
],
"iam_credential_removal_events": []
},
"tags": {"Owner": "platform-team", "Environment": "prod", "DataClassification": "confidential"},
"collection": {
"collected_at": "2026-05-28T12:00:00Z",
"collector_version": "aws-secretsmanager",
"collection_type": "config_dynamic",
"lookback_window": {"start": "2026-02-27T12:00:00Z", "end": "2026-05-28T12:00:00Z"},
"raw_payload_hashes": {"describe": "sha256:...", "policy": "sha256:...", "versions": "sha256:..."},
"errors": []
},
"policy_inputs": {}
}When AWS omits KmsKeyId for the AWS-managed default key, config.kms_key_id is emitted as the sentinel string aws/secretsmanager. Date fields are always strings and are "" when absent. When no resource policy exists, resource_policy is {"hash":"","document":null,"principals":[]} and resource_policy_present is false.
recovery_window_days defaults to 0 and is populated from a matched CloudTrail DeleteSecret event's requestParameters.recoveryWindowInDays when that event is available. Secrets Manager does not expose the original recovery-window setting directly on DescribeSecret.
CONFIG evidence supports rotation, vendor credential, confidentiality, and privacy policy bundles through DescribeSecret, GetResourcePolicy, ListSecretVersionIds, and tags from DescribeSecret. Fields include rotation state, rotation rules, KMS key ID string, owning service, replication status, version stages, resource policy principals, and hashed name/description.
DYNAMIC evidence uses a 90-day default CloudTrail lookback. Secrets Manager events are filtered to RotateSecret, PutSecretValue, UpdateSecret, UpdateSecretVersionStage, DeleteSecret, RestoreSecret, PutResourcePolicy, DeleteResourcePolicy, TagResource, UntagResource, CreateSecret, and GetSecretValue. IAM credential-removal events are filtered to DeleteUser, DeleteAccessKey, DetachUserPolicy, RemoveUserFromGroup, DeleteRole, DetachRolePolicy, and RemoveRoleFromInstanceProfile.
Secrets Manager events are matched to a secret by ARN, friendly name with suffix, or friendly name without suffix using substring checks against the raw CloudTrail event during collection. This is intentional because many Secrets Manager event types put the identifier in requestParameters rather than the structured Resources list.
IAM credential-removal events are account-wide. The plugin parses affected userName, roleName, userArn, and roleArn from requestParameters and attaches the event only to secrets whose resource-policy principal strings substring-match those identifiers. Unmatched IAM events are dropped.
Only normalized event fields are emitted; raw CloudTrail payloads are not included in Rego input.
Target-level failures, such as ListSecrets or CloudTrail lookup failure for an account/region, are stored once under target scope. They are not copied onto every secret.
Per-secret failures, such as a GetResourcePolicy error for one ARN, are attached only to that secret's collection.errors. ResourceNotFoundException and empty resource policies are valid no-policy states, not errors.
KMS key policy, key rotation, and grants are collected by plugin-aws-kms; this plugin records only the kms_key_id string from Secrets Manager. IAM principal resolution belongs to plugin-aws-iam; this plugin records policy principal strings and CloudTrail IAM-event ARNs only. Decrypted secret material is never read.
make build
make test