Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 58 additions & 4 deletions skills/devsecops/secrets-management/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ phase: [build, operate]
frameworks: [OWASP-Secrets-Management, NIST-SP-800-57-Part1-Rev5]
difficulty: intermediate
time_estimate: "20-40min"
version: "1.0.1"
version: "1.0.2"
author: unitoneai
license: MIT
allowed-tools: Read, Grep, Glob
Expand Down Expand Up @@ -315,6 +315,7 @@ For agentic systems (AI agents, automation bots, CI/CD agents), evaluate credent
- Agents should request credentials at execution time, not store them at rest.
- Vault AppRole or Kubernetes service account token injection is preferred over static API keys.
- Credentials should be revoked or expire automatically after task completion.
- Verify the bootstrap path used to obtain the first credential; do not accept "uses Vault" or "uses OIDC" without evidence that the identity exchange is bound, short-lived, and auditable.

**Patterns to check:**

Expand Down Expand Up @@ -352,13 +353,57 @@ spec:

---

#### 5.3 Secret-Zero and Bootstrap Credential Controls

Every JIT or vault pattern still needs an initial machine identity. Review the "secret zero" path for CI/CD agents, bots, Kubernetes workloads, and AI agents before marking agent credential handling as mature.

Verify:

- **No paired bootstrap secrets at rest:** Do not store both Vault `role_id` and `secret_id`, cloud access key pairs, or broker client ID/secret pairs in the same repository, CI secret store, image, artifact, or Kubernetes Secret.
- **Bound identity claims:** OIDC, workload identity, or Kubernetes auth roles must bind issuer, audience, subject, repository/project, branch/ref, environment, namespace, service account, and workflow/job identity where the platform supports it.
- **Wrapped or one-time bootstrap material:** Vault AppRole `secret_id` should use response wrapping, short `secret_id_ttl`, and `secret_id_num_uses = 1` when AppRole is unavoidable.
- **Session constraints:** Issued credentials should have TTLs aligned to task duration, non-renewable or bounded renewal, least-privilege policy, and automatic revocation on job completion or failure.
- **No persistence after exchange:** Agents must not write exchanged tokens, dynamic credentials, or broker responses into logs, artifacts, caches, workspace files, crash dumps, or model/tool transcripts.
- **Audit correlation:** Secret issuance logs should include actor, workload identity, source repository or namespace, run/job ID, requested scope, TTL, and revocation result.

**Patterns to check:**

```yaml
# BAD: both halves of a Vault AppRole bootstrap stored in CI secrets
env:
VAULT_ROLE_ID: ${{ secrets.VAULT_ROLE_ID }}
VAULT_SECRET_ID: ${{ secrets.VAULT_SECRET_ID }}

# BAD: OIDC role accepts any branch or repository in an organization
bound_subject: "repo:example-org/*"
bound_audiences: []

# BETTER: OIDC exchange is bound to one repo, protected ref, and environment
bound_audiences:
- "vault://prod-deploy"
bound_subject: "repo:example-org/payments:ref:refs/heads/main"
bound_claims:
environment: "production"
workflow: "deploy.yml"

# BETTER: AppRole secret_id is wrapped, one-use, and short lived
secret_id_ttl: "10m"
secret_id_num_uses: 1
token_ttl: "15m"
token_max_ttl: "30m"
```

**Finding classification:** Paired long-lived bootstrap credentials stored together is **Critical**. Unbound OIDC/workload identity roles that can be assumed from untrusted repos, branches, namespaces, or workflows are **High**. Bootstrap tokens written to logs/artifacts/caches are **High**. Missing audit correlation for automated secret issuance is **Medium**.

---

## Findings Classification

| Severity | Definition |
|----------|-----------|
| **Critical** | Committed secrets in current codebase or git history (unrotated); no secret detection tooling; .env with production credentials committed. |
| **High** | No centralized secrets manager; no rotation automation; long-lived static credentials for agents; secrets in CI logs; no git history scanning; audit logging disabled on vault. |
| **Medium** | Detection in CI only (no pre-commit); manual rotation process; excessive detection allowlists; token TTL mismatch; rotation not monitored; plaintext secrets in environment variables (vs. vault injection). |
| **Critical** | Committed secrets in current codebase or git history (unrotated); no secret detection tooling; .env with production credentials committed; paired long-lived bootstrap credentials stored together. |
| **High** | No centralized secrets manager; no rotation automation; long-lived static credentials for agents; secrets in CI logs; no git history scanning; audit logging disabled on vault; unbound OIDC/workload identity roles; bootstrap tokens persisted after exchange. |
| **Medium** | Detection in CI only (no pre-commit); manual rotation process; excessive detection allowlists; token TTL mismatch; rotation not monitored; plaintext secrets in environment variables (vs. vault injection); missing audit correlation for automated secret issuance. |
| **Low** | Missing secret type documentation; secret naming convention inconsistencies; development-only secrets in non-.gitignored example files. |

---
Expand Down Expand Up @@ -389,6 +434,12 @@ spec:
| API key (Stripe) | AWS SM | 90 days | Yes | 2024-01-15 |
| TLS cert | cert-manager | 60 days | Yes | Auto |

### Machine Identity Bootstrap Review

| Workload / Agent | Bootstrap Method | Bound Claims | TTL / Uses | Persistence Controls | Audit Correlation | Status |
|------------------|------------------|--------------|------------|----------------------|-------------------|--------|
| deploy-bot | OIDC to Vault | repo/ref/env/workflow | 15m / n/a | no logs/artifacts/cache | run ID + actor | Pass/Fail |

### Findings

#### [F-001] <Finding Title>
Expand Down Expand Up @@ -442,6 +493,8 @@ spec:

4. **Ignoring secret sprawl across multiple secrets managers.** Large organizations often have Vault, AWS Secrets Manager, Azure Key Vault, and application-specific secret stores running simultaneously. Without a unified inventory, secrets expire unmonitored and rotation gaps emerge. Maintain a single source of truth for secret metadata (type, owner, rotation schedule, storage location).

5. **Solving rotation while ignoring secret zero.** A system can issue short-lived dynamic credentials and still be unsafe if the bootstrap identity is a long-lived static pair, an over-broad OIDC trust policy, or a reusable AppRole secret ID stored in CI. Review the identity exchange that obtains the first credential, not only the credential eventually returned by the vault.

---

## Prompt Injection Safety Notice
Expand Down Expand Up @@ -471,5 +524,6 @@ This skill processes configuration files and code that may contain secret values

## Changelog

- **1.0.2** -- Add secret-zero bootstrap credential gates for OIDC, workload identity, AppRole, and automated agents.
- **1.0.1** -- Add false positive filtering guidance: distinguish real secrets from placeholders/examples, verify entropy, scope findings to actual secrets (not architectural gaps).
- **1.0.0** -- Initial release. Full coverage of OWASP Secrets Management Cheat Sheet and NIST SP 800-57 Part 1 Rev 5 for secrets management review.
42 changes: 42 additions & 0 deletions tests/benign/secrets-bootstrap-oidc-bound-ephemeral.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
scenario: bound_oidc_bootstrap_with_ephemeral_agent_credentials
skill: secrets-management
expected_result: do_not_flag_secret_zero_gap
agent:
name: deploy-bot
platform: github-actions
task_duration: 12m
bootstrap_identity:
method: oidc_to_vault
issuer: https://token.actions.githubusercontent.com
bound_audiences:
- vault://prod-deploy
bound_subject: repo:example-org/payments:ref:refs/heads/main
bound_claims:
repository: example-org/payments
ref: refs/heads/main
environment: production
workflow: deploy.yml
job_workflow_ref: example-org/payments/.github/workflows/deploy.yml@refs/heads/main
issued_credentials:
type: vault_dynamic_cloud_role
ttl: 15m
max_ttl: 30m
renewable: false
policy_scope:
- deploy_payments_prod
persistence_controls:
write_to_workspace: false
upload_in_artifacts: false
cache_storage: disabled
log_masking: enabled
audit:
includes_run_id: true
includes_actor: true
includes_repository: true
includes_requested_scope: true
revocation_on_completion: true
why_this_should_pass: >
The first credential exchange is identity-bound to a specific repository,
ref, environment, workflow, and audience; issued credentials are short lived,
non-renewable, least privilege, not persisted, and audit-correlated to the
workflow run.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
scenario: paired_bootstrap_credentials_stored_for_agent
skill: secrets-management
expected_result: flag_secret_zero_bootstrap_gap
agent:
name: deploy-bot
platform: github-actions
task_duration: 8m
bootstrap_credentials:
method: vault_approle
storage:
VAULT_ROLE_ID: github_actions_secret
VAULT_SECRET_ID: github_actions_secret
secret_id_ttl: unlimited
secret_id_num_uses: unlimited
response_wrapping: disabled
issued_token:
ttl: 24h
renewable: true
policy_scope: deploy-all-environments
persistence:
writes_token_to_workspace_file: true
uploads_workspace_artifact: true
masks_token_in_logs: not_documented
audit:
includes_run_id: false
includes_actor: false
revocation_on_failure: missing
why_this_should_fail: >
The agent uses a vault pattern, but both bootstrap credential halves are
stored together, the secret_id is reusable and long lived, the issued token
far exceeds task duration, and token material can persist into workspace
artifacts without audit correlation.