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
32 changes: 32 additions & 0 deletions skills/devsecops/secrets-management/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,19 @@ spec:

**Finding classification:** Agents using long-lived static credentials is **High**. No JIT credential mechanism for automated systems is **Medium**. Token TTL exceeding 10x task duration is **Medium**.

#### 5.3 Secret-Zero Bootstrap Evidence Gate

Do not credit a system as using short-lived or JIT credentials until the review proves how the workload obtains its first machine credential. Vault, cloud STS, OIDC, and brokered credentials still fail when bootstrap material is paired, over-broad, persistent, or unaudited.

- `SEC-ZERO-01` - Document the bootstrap exchange for each CI/CD agent, bot, Kubernetes workload, AI agent, and deployment job: identity provider, trust boundary, caller identity, target secrets engine, requested role, and first credential material.
- `SEC-ZERO-02` - Prove paired bootstrap material is separated. Do not store both halves of an AppRole, client credential pair, key pair, recovery token, or broker credential in the same repository secret, CI variable group, Kubernetes Secret, environment file, image layer, or agent profile.
- `SEC-ZERO-03` - For OIDC or workload identity, require issuer, audience, subject, repository/project, organization, branch/ref, environment, workflow/job, namespace, service account, and runner pool claims to be explicitly bounded where the provider supports them. Wildcard repository, branch, environment, or workflow bindings must be treated as over-broad.
- `SEC-ZERO-04` - When AppRole or an equivalent bootstrap secret is unavoidable, require response wrapping, one-use or tightly bounded `secret_id`, short TTL, source restrictions, separate delivery channels, and rotation/revocation evidence.
- `SEC-ZERO-05` - Bind issued credentials to task duration and least privilege: requested scope, policy, lease ID, credential TTL, renewal rules, and revocation on job completion must match the workload's expected runtime.
- `SEC-ZERO-06` - Verify exchanged credentials are not persisted to logs, artifacts, caches, workspace files, dependency caches, container layers, crash dumps, debug bundles, model/tool transcripts, or CI summaries.
- `SEC-ZERO-07` - Require audit correlation for automated issuance and revocation: run ID, actor, repository/project, ref, workflow/job, namespace/service account, requested role, requested scope, lease ID, issue timestamp, revoke timestamp, and outcome.
- `SEC-ZERO-08` - Cap the finding at Not Evaluable when the bootstrap path is unknown; High when paired bootstrap material is stored together or over-broad OIDC claims can mint credentials for untrusted workloads; Medium when TTL, non-persistence, or audit correlation evidence is incomplete.

---

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

### Secret-Zero Bootstrap Review

| Workload | Bootstrap Method | Identity Binding | Paired Material Separated | Issued TTL / Scope | Non-Persistence Evidence | Audit Correlation | Status |
|----------|------------------|------------------|---------------------------|--------------------|--------------------------|------------------|--------|
| <job/agent> | <OIDC/AppRole/workload identity/broker> | <claims or caller identity> | <yes/no/unknown> | <duration + policy> | <logs/artifacts/caches checked> | <run/actor/lease/revoke> | <Pass/Fail/NE> |

| Gate | Evidence Required | Result | Finding |
|------|-------------------|--------|---------|
| SEC-ZERO-01 | Complete bootstrap exchange inventory for automated workloads | <Pass/Fail/NE> | <notes> |
| SEC-ZERO-02 | Paired bootstrap material separated across trust boundaries | <Pass/Fail/NE> | <notes> |
| SEC-ZERO-03 | OIDC/workload identity claims bounded to issuer, audience, subject, repo/project, ref, environment, workflow/job, namespace, and service account | <Pass/Fail/NE> | <notes> |
| SEC-ZERO-04 | AppRole or equivalent fallback uses wrapping, one-use/short TTL material, source restrictions, separate delivery, and revocation | <Pass/Fail/NE> | <notes> |
| SEC-ZERO-05 | Issued credential TTL, scope, lease, and revocation match the task | <Pass/Fail/NE> | <notes> |
| SEC-ZERO-06 | Exchanged credentials do not persist in logs, artifacts, caches, files, images, crash dumps, transcripts, or summaries | <Pass/Fail/NE> | <notes> |
| SEC-ZERO-07 | Issuance and revocation audit records correlate to run, actor, ref, role, scope, lease, and outcome | <Pass/Fail/NE> | <notes> |
| SEC-ZERO-08 | Status cap applied for unknown, paired, over-broad, persistent, or unaudited bootstrap paths | <Pass/Fail/NE> | <notes> |

### Findings

#### [F-001] <Finding Title>
Expand Down Expand Up @@ -442,6 +472,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 but not secret zero.** Dynamic credentials are only as strong as the bootstrap exchange that mints them. Storing both AppRole halves together, allowing broad OIDC claim matches, or writing exchanged tokens into artifacts and logs turns a vault or broker into a credential vending path for untrusted workloads.

---

## Prompt Injection Safety Notice
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
{
"case_id": "secrets_bounded_oidc_bootstrap_non_persistent_tokens",
"description": "Automated workloads obtain first credentials through bounded OIDC or separated one-use bootstrap flows, keep issued credentials short-lived, and prove tokens do not persist into logs, artifacts, caches, files, images, or transcripts.",
"review_scope": {
"repositories": [
"payments-api",
"billing-worker"
],
"environments": [
"staging",
"production"
],
"workloads_reviewed": [
"github-actions-deploy",
"kubernetes-billing-worker",
"ai-remediation-agent"
]
},
"bootstrap_exchanges": [
{
"workload": "github-actions-deploy",
"bootstrap_method": "github_oidc_to_cloud_sts",
"identity_provider": "https://token.actions.githubusercontent.com",
"trust_boundary": "GitHub Actions hosted runner to cloud STS",
"caller_identity": {
"repository": "acme/payments-api",
"organization": "acme",
"ref": "refs/heads/main",
"environment": "production",
"workflow": "deploy.yml",
"job": "deploy",
"runner_pool": "github-hosted"
},
"identity_binding": {
"issuer": "https://token.actions.githubusercontent.com",
"audience": "sts.cloud.example",
"subject": "repo:acme/payments-api:environment:production",
"repository": "acme/payments-api",
"ref": "refs/heads/main",
"environment": "production",
"workflow": "deploy.yml",
"job": "deploy",
"wildcards_allowed": false
},
"paired_material": {
"stored_together": false,
"evidence": [
"cloud-trust-policy-export-2026-06-08",
"github-environment-secret-inventory-2026-06-08"
]
},
"issued_credential": {
"requested_role": "payments-prod-deploy",
"policy": "deploy-artifact-and-restart-service",
"ttl_minutes": 45,
"expected_runtime_minutes": 18,
"renewable": false,
"revocation": "auto-expire plus post-job session invalidation"
},
"non_persistence_evidence": {
"logs_scanned": true,
"artifacts_scanned": true,
"caches_scanned": true,
"workspace_files_scanned": true,
"container_layers_scanned": true,
"crash_dumps_scanned": true,
"transcripts_scanned": true,
"findings": []
},
"audit_correlation": {
"run_id": "gha-run-991244",
"actor": "release-bot",
"ref": "refs/heads/main",
"requested_scope": "deploy-artifact-and-restart-service",
"lease_id": "sts-session-2026-06-08-991244",
"issue_timestamp": "2026-06-08T09:14:02Z",
"revoke_timestamp": "2026-06-08T09:34:21Z",
"outcome": "issued_and_expired"
},
"status": "Pass"
},
{
"workload": "kubernetes-billing-worker",
"bootstrap_method": "kubernetes_workload_identity_to_vault",
"identity_provider": "kubernetes_service_account_jwt",
"trust_boundary": "production namespace service account to Vault Kubernetes auth",
"caller_identity": {
"cluster": "prod-us-east",
"namespace": "billing",
"service_account": "billing-worker",
"pod_label_selector": "app=billing-worker",
"image_digest": "registry.example/billing-worker@sha256:example-prod-image-digest-20260608"
},
"identity_binding": {
"bound_service_account_names": [
"billing-worker"
],
"bound_namespaces": [
"billing"
],
"bound_audiences": [
"vault"
],
"bound_image_digest": true,
"wildcards_allowed": false
},
"paired_material": {
"stored_together": false,
"evidence": [
"vault-kubernetes-auth-role-export-2026-06-08",
"k8s-secret-inventory-billing-namespace-2026-06-08"
]
},
"issued_credential": {
"requested_role": "billing-db-readwrite",
"policy": "database/creds/billing-worker",
"ttl_minutes": 20,
"expected_runtime_minutes": 10,
"renewable": false,
"revocation": "lease revoke on pod termination"
},
"non_persistence_evidence": {
"logs_scanned": true,
"artifacts_scanned": "not_applicable",
"caches_scanned": true,
"workspace_files_scanned": true,
"container_layers_scanned": true,
"crash_dumps_scanned": true,
"transcripts_scanned": "not_applicable",
"findings": []
},
"audit_correlation": {
"run_id": "pod-uid-7d65b",
"actor": "system:serviceaccount:billing:billing-worker",
"ref": "image-digest-sha256-11112222",
"requested_scope": "database/creds/billing-worker",
"lease_id": "database/creds/billing-worker/lease-48291",
"issue_timestamp": "2026-06-08T10:22:01Z",
"revoke_timestamp": "2026-06-08T10:34:04Z",
"outcome": "issued_and_revoked"
},
"status": "Pass"
},
{
"workload": "ai-remediation-agent",
"bootstrap_method": "brokered_oidc_to_vault",
"identity_provider": "internal workload broker",
"trust_boundary": "agent orchestrator to vault broker",
"caller_identity": {
"agent_id": "remediation-agent-prod",
"tool_policy": "ticket-comment-and-readonly-repo",
"environment": "production",
"approval_id": "SECOPS-APPROVAL-2026-06-08"
},
"identity_binding": {
"issuer": "agent-orchestrator",
"audience": "vault-broker",
"subject": "agent:remediation-agent-prod",
"environment": "production",
"workflow": "approved-remediation",
"wildcards_allowed": false
},
"paired_material": {
"stored_together": false,
"evidence": [
"broker-policy-export-2026-06-08",
"agent-profile-secret-scope-review-2026-06-08"
]
},
"issued_credential": {
"requested_role": "readonly-repo-ticket-commenter",
"policy": "read-repo-and-comment-ticket",
"ttl_minutes": 15,
"expected_runtime_minutes": 6,
"renewable": false,
"revocation": "broker revokes lease at task completion"
},
"non_persistence_evidence": {
"logs_scanned": true,
"artifacts_scanned": true,
"caches_scanned": true,
"workspace_files_scanned": true,
"container_layers_scanned": true,
"crash_dumps_scanned": true,
"transcripts_scanned": true,
"findings": []
},
"audit_correlation": {
"run_id": "agent-task-55410",
"actor": "remediation-agent-prod",
"ref": "ticket-SEC-2244",
"requested_scope": "read-repo-and-comment-ticket",
"lease_id": "broker-lease-55410",
"issue_timestamp": "2026-06-08T12:00:01Z",
"revoke_timestamp": "2026-06-08T12:07:30Z",
"outcome": "issued_and_revoked"
},
"status": "Pass"
}
],
"expected_gate_results": {
"SEC-ZERO-01": "Pass",
"SEC-ZERO-02": "Pass",
"SEC-ZERO-03": "Pass",
"SEC-ZERO-04": "Pass",
"SEC-ZERO-05": "Pass",
"SEC-ZERO-06": "Pass",
"SEC-ZERO-07": "Pass",
"SEC-ZERO-08": "Pass"
},
"expected_assessment": {
"severity": "None",
"status_cap": "None",
"finding": "All reviewed automated workloads have bounded bootstrap identity, separated bootstrap material, short-lived least-privilege issued credentials, non-persistence proof, and issuance/revocation audit correlation."
}
}
Loading