Security Snapshot, pipeline before and after hardening, Evidence Pack ready for the auditor — this is what our work looks like. No abstractions.
This is what the document looks like after a CI/CD audit — GitHub Actions, AWS, Docker container. Three tabs: remediation priorities, technical details, roadmap.
| Level | Finding | Description | Standard |
|---|---|---|---|
| ■ CRITICAL | Static AWS IAM keys in GitHub Secrets | 3 keys, rotation unknown. No CloudTrail alerts on usage outside pipeline. | DORA §16.2(a) |
| ■ CRITICAL | No GitHub Actions pinning to SHA | 12/15 actions use tags (@v1, @v3) — vulnerable to supply chain attack if maintainer account is compromised. | SLSA L2 |
| ■ CRITICAL | GitHub PAT token leak in Git history | Commit a3f9c12 (2025-11-08): .env file with active token. Token not revoked — org access still active. | NIS2 §21(d) |
| ■ HIGH | No Docker artifact signing (Cosign) | Production images without cryptographic signature. Auditor cannot verify provenance — VRA blocked. | DORA §16.2(b) |
| ■ HIGH | Direct push to main without code review | No branch protection rules. 3 commits in the last 30 days bypassed review — missing separation of duties. | SOC 2 CC6.1 |
| ■ MEDIUM | No SBOM generation at build time | No workflow generates Software Bill of Materials — required by enterprise VRA and NIS2 / CRA. | NIS2 / VRA |
| ■ MEDIUM | GITHUB_TOKEN with write:all permissions (default) | Default org configuration: permissions: write-all. Every job has full write access — least privilege violated. | SLSA L1 |
| ■ LOW | No periodic repository permission review | 4 external collaborator accounts with Write access — no offboarding process or 30-day review... | SOC 2 CC6.2 |
| Level | Finding | Technical details & evidence | Tool |
|---|---|---|---|
| ■ CRITICAL | Static AWS IAM keys | Secret: AWS_ACCESS_KEY_ID = AKIA... (prefix AKIA = long-term IAM user key). IAM Policy: AdministratorAccess — full AWS account access. Last activity: 2026-03-01 (query to S3 prod). Rotation: no history in IAM Credentials Report. |
TruffleHog + AWS CLI |
| ■ CRITICAL | Actions without SHA pinning | Example: uses: actions/checkout@v4 — tag v4 can be moved by the repo owner without warning. Secure form: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683. Affects 12/15 actions in workflow. |
Manual analysis + zizmith |
| ■ CRITICAL | Wyciek PAT w Git history | ghp_xK9mL2... (GitHub PAT, scope: repo:write) — detected in commit a3f9c12, file scripts/.env.deploy. Token active — verification: curl -H "Authorization: token ghp_xK9..." https://api.github.com/user → HTTP 200. Immediate revocation required. |
TruffleHog v3 |
| ■ HIGH | No Docker artifact signing (Cosign) | Images built by pipeline without cosign sign step. No signature verification in Kubernetes admission controller — deploy possible from any source. Auditor cannot confirm container provenance in production. | Workflow analysis |
| ■ HIGH | Direct push to main without code review | Branch protection rules inactive on main. Git log: 3 commits directly to main in the last 30 days (hashe: b4e2f1a, c9d3e7b, f1a8c2d) — none went through a PR. Missing separation of duties required by SOC 2 CC6.1. |
Git log analysis |
| ■ MEDIUM | No SBOM generation at build time | None of the 3 workflows contain a SBOM generation step. Syft or Trivy SBOM can be added as a step after docker build. Missing SBOM blocks compliance with NIS2 / CRA Art.13 and is a common reason for rejection in enterprise VRA. |
Workflow analysis |
| ■ MEDIUM | GITHUB_TOKEN z uprawnieniami write:all | Default org configuration: permissions: write-all. Every job has full write access to the repo — least privilege violated. Secure configuration: permissions: contents:read, id-token:write per-job. Change: approx. 1h... |
Org configuration audit |
| Phase | Action | Business impact |
|---|---|---|
|
Phase 1
Immediately
|
Revoke active PAT token and IAM keys
Revoke
ghp_xK9... w GitHub Settings. Deactivate AKIA keys in IAM Console. Verify no active sessions remain. |
Close active vulnerability — unauthorized access no longer possible |
|
Phase 1
Week 1
|
Pin actions to SHA + branch protection rules
Replace tags with SHA in 12 actions. Enable required reviews on main: min. 2 approvals, block direct push, required status checks.
|
Block supply chain attack. Separation of duties for SOC 2 CC6.1 |
|
Phase 2
Week 1–2
|
Deploy OIDC — eliminate static AWS keys
GitHub OIDC provider in AWS IAM. Roles with minimal scope per-repo, per-branch. Remove variables
AWS_ACCESS_KEY_ID z GitHub Secrets. |
DORA §16.2(a) compliance. Zero long-lived secrets in CI/CD |
|
Phase 2
Week 2
|
Cosign + SBOM — artifact signing and inventory
Sigstore/Cosign step in pipeline after docker build. Syft for CycloneDX SBOM generation. Archival to S3 on every release.
|
DORA §16.2(b) compliance, NIS2 supply chain. Unblock enterprise VRA |
|
Phase 3
Week 3
|
Restrict GITHUB_TOKEN permissions to least privilege
Konfiguracja
permissions per-job: contents:read, id-token:write. Disable default write-all at org level. |
Eliminate excessive repo access — reduce blast radius in case of workflow compromise |
|
Phase 3
Week 3–4
|
Runner isolation + log export to SIEM
Ephemeral runners in isolated network (no access to RDS/Redis). CloudWatch log forwarding with 365-day retention. Anomaly alerts.
|
Eliminacja ryzyka lateralnego. Audit trail zgodny z DORA i SOC 2 |
|
Phase 3
Month 2
|
Policy-as-Code + full Evidence Pack
OPA Rego policies as mandatory gates. Automatic evidence mapping to DORA/NIS2/SOC 2. Evidence Pack PDF generation on every...
|
Full readiness for KNF audit and enterprise VRA without manual effort |
The same security problem — three platforms, three approaches to hardening. Choose your stack:
name: Deploy to Production on: [push] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@main # brak pinowania wersji akcji - name: Configure AWS env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET }} # static IAM keys — long-lived - name: Build & Push run: | docker build -t app . docker push $IMAGE_NAME # brak podpisywania obrazu - name: Deploy run: kubectl apply -f k8s/ # brak weryfikacji artefaktu przed deployem
name: Deploy to Production on: push: branches: [main] # tylko main, po code review permissions: id-token: write contents: read jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pinowany SHA — immutable - name: Configure AWS via OIDC uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::${{vars.AWS_ACCOUNT}}:role/github-oidc # OIDC — zero statycznych kluczy - name: Build, Sign & Push run: | docker build -t $IMAGE . docker push $IMAGE cosign sign --yes $IMAGE # Cosign — kryptograficzny podpis
# azure-pipelines.yml trigger: - main pool: vmImage: 'ubuntu-latest' # Secrets jako zmienne pipeline — statyczne, # visible in logs on misconfiguration variables: ARM_CLIENT_SECRET: $(arm-client-secret) ARM_CLIENT_ID: $(arm-client-id) ARM_SUBSCRIPTION_ID: 'a1b2c3d4-...' steps: - task: AzureCLI@2 inputs: azureSubscription: 'prod-service-connection' scriptType: bash script: | az acr build --registry $(acrName) \ --image myapp:$(Build.BuildId) . az webapp deploy --resource-group prod-rg \ --name my-webapp # Brak: approval gate, branch policy, # audytu zmian w pipeline YAML
echo in scripttrigger:
branches:
include: [main]
paths:
exclude: ['**.md']
pool:
name: 'CyberForge-Ephemeral'
# Self-hosted, ephemeral, isolated network
# Zero static secrets — OIDC Workload Identity
steps:
- task: AzureCLI@2
inputs:
azureSubscription: 'wif-acr-only'
# Service Connection z zakresem tylko do ACR
addSpnToEnvironment: false
scriptType: bash
script: |
az acr build --registry $(acrName) \
--image myapp:$(Build.BuildId) .
- task: ManualValidation@1
condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
inputs:
notifyUsers: '[email protected]'
instructions: 'Approve deployment to prod'
timeoutInMinutes: 60
- task: AzureCLI@2
inputs:
azureSubscription: 'wif-webapp-deploy-only'
scriptType: bash
script: az webapp deploy ...stages: - build - test - deploy # Secrets jako zmienne w CI/CD Settings — plaintext # available in EVERY job, EVERY branch variables: REGISTRY_TOKEN: $CI_REGISTRY_TOKEN DEPLOY_SSH_KEY: $SSH_PRIVATE_KEY KUBE_CONFIG: $KUBERNETES_CONFIG # all secrets available in all jobs test: stage: test image: node:latest # no pinning — "latest" changes without warning script: - npm install - npm test # no dependency scanning before test build: stage: build image: docker:latest services: - docker:dind script: - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA # no signing — anyone can replace the image # no SBOM — auditor has no component inventory deploy: stage: deploy script: - ssh -i $DEPLOY_SSH_KEY user@prod-server # statyczny klucz SSH — nie rotowany, nie audytowalny - kubectl apply -f k8s/ # brak weryfikacji artefaktu przed deployem only: - branches # deploy from EVERY branch — feature too
stages: - scan - build - attest - deploy # Zmienne maskowane i ograniczone do protected branches variables: COSIGN_YES: "true" DOCKER_IMAGE: docker:25.0.3-dind@sha256:4a6f... # pinowany SHA — immutable, deterministyczny secret-scan: stage: scan image: trufflesecurity/trufflehog:3.67.7 script: - trufflehog git --fail file://. # blokuje merge przy wykryciu sekretu build-sign: stage: build id_tokens: SIGSTORE_ID_TOKEN: aud: sigstore # OIDC token dla Cosign — bez statycznych kluczy script: - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA - cosign sign $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA - syft $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -o cyclonedx-json > sbom.cdx.json - cosign attest --predicate sbom.cdx.json --type cyclonedx $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA rules: - if: $CI_COMMIT_BRANCH == "main" deploy: stage: deploy environment: name: production when: manual # manual approval required in GitLab UI script: - cosign verify $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA # signature verification BEFORE deploy - kubectl apply -f k8s/ rules: - if: $CI_COMMIT_BRANCH == "main"
Below is a CycloneDX file with the same structure as production — this is exactly what the pipeline generates automatically on every build. The auditor gets this instead of a declaration that "we use secure libraries".
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"metadata": {
"timestamp": "2026-03-12T09:14:22Z",
"tools": [
{ "name": "syft", "version": "1.4.1", "vendor": "Anchore" }
],
"authors": [{ "name": "CyberForge CI Pipeline" }],
"component": {
"type": "container",
"name": "myapp",
"version": "2.4.1",
"purl": "pkg:oci/myapp@sha256:a3b1c9d2e8f47b6...",
"hashes": [{ "alg": "SHA-256", "content": "a3b1c9d2e8f47b6..." }],
"properties": [
{ "name": "syft:package:foundBy", "value": "docker-image-cataloger" },
{ "name": "ci:build_id", "value": "1337" },
{ "name": "ci:git_sha", "value": "d4e5f6a7b8c9..." },
{ "name": "ci:git_ref", "value": "refs/heads/main" }
]
}
},
"components": [
{
"type": "library",
"name": "express",
"version": "4.18.3",
"purl": "pkg:npm/[email protected]",
"licenses": [{ "license": { "id": "MIT" } }],
"hashes": [{ "alg": "SHA-256", "content": "e3b0c44298fc1c149afb..." }],
// ↑ auditor can verify hash independently from npm registry
"externalReferences": [
{ "type": "website", "url": "https://expressjs.com" },
{ "type": "vcs", "url": "https://github.com/expressjs/express" }
]
},
{
"type": "library",
"name": "lodash",
"version": "4.17.21",
"purl": "pkg:npm/[email protected]",
"licenses": [{ "license": { "id": "MIT" } }],
"hashes": [{ "alg": "SHA-256", "content": "d7ff4f98feae9a0a..." }]
// brak znanych CVE — zweryfikowano Trivy 0.51.1 @ 2026-03-12T09:13:44Z
},
{
"type": "library",
"name": "openssl",
"version": "3.0.13",
"purl": "pkg:deb/debian/[email protected]",
"licenses": [{ "license": { "id": "Apache-2.0" } }],
"hashes": [{ "alg": "SHA-256", "content": "c8f1a2b3d4e5..." }]
// pakiet OS (debian) — w 3.0.13 brak HIGH/CRITICAL CVE wg NVD
}
// ... 244 more components (npm + debian packages + OS layers)
],
"vulnerabilities": []
// ↑ empty = Trivy scan found no CVE in this build
}
$ cosign verify-attestation \
--type cyclonedx \
--certificate-identity "https://github.com/firma/myapp/.github/workflows/release.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
myapp@sha256:a3b1c9d2e8f47b6...
Verification for myapp@sha256:a3b1c9d2e8f47b6... --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- Existence of the claims in the transparency log was verified (Rekor)
- The signatures were verified against the specified public key
- Certificate identity matched workflow: release.yml @ refs/heads/main
RekorEntry: https://rekor.sigstore.dev/api/v1/log/entries/362f8ecba0f2...
IntegratedTime: 2026-03-12T09:14:38Z · LogIndex: 182736450
Issuer: https://token.actions.githubusercontent.com (GitHub OIDC)
✓ SBOM is cryptographically linked to a specific container SHA on prod
✓ Signature originates exclusively from a trusted pipeline on the main branch
✓ Rekor entry is immutable — auditor can verify without your assistance
This is what the auditor, compliance team or Enterprise client receives. Each technical control with evidence and a reference to a specific article. Select a regulation:
| DORA Requirement | Technical control | Technical evidence | Status |
|---|---|---|---|
| Art.16 §2(a) ICT asset management and classification |
Software component inventory (SBOM) | CycloneDX 1.5 SBOM generated by Syft on every build. Archived in S3 with 365-day retention. Every release has a signed SBOM with full list of libraries, versions and licenses. | ✓ met |
| Art.16 §2(b) Ochrona i zapobieganie incydentom ICT |
Vulnerability scanning and critical CVE blocking | Trivy scanner runs on every PR and build. Security gate blocks merge when CRITICAL or HIGH CVE detected without accepted exception. Last scan: 0 critical, 2 high (accepted with justification). | ✓ met |
| Art.16 §2(c) Anomaly and incident detection |
Secret leak and pipeline anomaly alerts | TruffleHog scan on every push — blocks on secret detection. GHAS: 3 alerts in last 90 days, all closed in <24h. SIEM integration and runtime alert routing require configuration outside pipeline (Phase F). | ~ partial |
| Art.16 §2(d) Access management and authentication |
OIDC Workload Identity — static secret elimination | GitHub Actions ↔ AWS via OIDC federation. Zero long-lived IAM keys in pipeline. Each token valid max 3600s, scope limited to specific repo and branch. CloudTrail log of every assume-role. | ✓ met |
| Art.16 §2(e) Business continuity and ICT backup |
Pipeline configuration and secrets backup policies | Full pipeline configuration as code (IaC) in Git — environment recovery <30 min. AWS Secrets Manager: automatic backup every 24h to second region. Last recovery test: 2026-02-15, result: OK. | ~ partial |
| Art.16 §2(f) Separation of duties i kontrola zmian |
Branch protection, wymagane approvals, audit trail | Branch protection rules on main: min. 2 approvals (incl. 1 from security team), block direct push, required status checks (tests + security gate). Every pipeline YAML change requires separate review by senior engineer. | ✓ met |
| Art.16 §2(g) ICT system resilience testing |
Periodic pipeline testing for failure scenarios | Quarterly chaos engineering: runner failure simulation, secret rotation under load, procedural rollback test. Results archived... | ~ partial |
| NIS2 Requirement | Technical control | Technical evidence | Status |
|---|---|---|---|
| Art.21 §2(a) Information systems security policies |
Policy-as-Code defined in pipeline — integration as mandatory gate deployed during Hardening Sprint | OPA Rego policies defined and tested in repository: 23 rules with unit test coverage. Workflow integration as blocking gate configured per deployment. Formal security policy documentation and risk register require a separate organizational process. | ~ partial |
| Art.21 §2(b) Incident handling — detection and response |
Automated detection in pipeline — IR runbooks require organizational process | GHAS secret scanning + Dependabot alerts integrated with Jira (auto-ticket on HIGH/CRITICAL). MTTR from last 90 days: 18h for critical. Formal IR runbooks, incident classification and regulator escalation require configuration outside pipeline. | ~ partial |
| Art.21 §2(d) Supply chain security |
Action pinning to SHA, artifact provenance verification | All external GitHub Actions pinned to full commit SHA (not tag). Pipeline addresses SLSA Level 2 requirements: signed provenance for every build generated by a trusted platform. Cosign Docker image verification before deploy — blocked on signature mismatch. | ✓ met |
| Art.21 §2(e) Security in system acquisition and maintenance |
SAST, SCA i IaC scanning jako mandatory gates | CodeQL (SAST) runs on every PR — results visible in code review. Trivy SCA: dependency and image scanning. Checkov IaC: Terraform verification before apply. All 3 are required status checks. | ✓ met |
| Art.21 §2(f) Policies and procedures for assessing effectiveness of measures |
Pipeline security metrics — monthly reporting | Grafana dashboard with KPI: number of blocked builds (security gate), vulnerability MTTR, % of pipelines with active scan. Report generated automatically on the 1st of each month for CISO. | ~ partial |
| Art.21 §2(h) Human resources security, training |
Security Champions program and developer onboarding | Pipeline hardening runbook delivered to team. 4h handover session with senior engineers. Operational documentation in Confluence. | ~ partial |
| Art.21 §2(i) Kryptografia i szyfrowanie |
Secret encryption at rest and in transit | AWS Secrets Manager (AES-256, KMS CMK). TLS 1.3 na wszystkich endpointach pipeline. Rotacja kluczy KMS co 90 dni. Certyfikaty... | ✓ met |
| Kryterium SOC 2 | Technical control | Technical evidence | Status |
|---|---|---|---|
| CC6.1 Logical and physical access controls |
Repo and pipeline access control — IAM governance outside pipeline | GitHub org: SAML SSO required, MFA enforced. GITHUB_TOKEN scope granular (contents:read, packages:write). OIDC eliminates static CI/CD keys. Physical access controls and periodic access reviews require separate organizational procedures. | ~ partial |
| CC6.2 Authentication prior to access |
OIDC + MFA for all pipeline entry points | No static keys in CI/CD — authentication exclusively via OIDC federation (GitHub ↔ AWS/Azure). AWS Console: MFA required policy (SCP). Every login audited in CloudTrail with 1-year retention. | ✓ met |
| CC7.1 Detection of security events |
Detection in pipeline — runtime monitoring and SIEM require org configuration | TruffleHog: scan of every commit + Git history. GHAS: secret scanning + code scanning. Cosign verification before deploy. SIEM integration (Elastic/GuardDuty), on-call runbooks and coverage of systems outside pipeline require separate configuration. | ~ partial |
| CC8.1 Change management |
Audytowalny proces zmian w pipeline i infrastrukturze | All changes via PR (Git flow). Pipeline YAML: required review by 2 people, incl. 1 senior security engineer. Terraform: plan review before apply, state in S3 with versioning. Change log exported to auditors on request. | ✓ met |
| CC9.1 Risk assessment — vendor management |
Dependency and action verification in pipeline — vendor governance outside pipeline | GitHub Actions pinned to SHA. Trivy SCA scans npm/python/go dependencies on every build. SBOM + provenance as supply chain evidence. Formal vendor management program, due diligence and contract review require a separate process. | ~ partial |
| A1.2 Availability — recovery procedures |
Pipeline and environment reproducibility from code (IaC) | Full infrastructure as Terraform — recovery time <45 min (measured). Ephemeral runners: auto-provisioning from AMI. GitHub Actions workflows: idempotent, every run deterministic. RTO <2h, RPO = 0 (config w Git). | ~ partial |
| PI1.1 Processing integrity |
Artifact integrity: digital signatures and verification | Cosign signs every Docker image at build time. Signature verification before deploy (Kubernetes admission controller). Rekor transparency log — every signature publicly verifiable... | ✓ met |
| VRA questionnaire question | Technical evidence | Details | Answer |
|---|---|---|---|
| Do you use MFA for all users with access to source code? | GitHub org: MFA enforced | GitHub Organization policy: „Require two-factor authentication for everyone". Verification: export of org member list — 100% have active 2FA. SAML SSO integrated with Entra ID (Azure AD). Configuration screenshot in Appendix A. | ✓ Yes |
| How do you manage secrets and API keys in your CI/CD process? | OIDC + AWS Secrets Manager | Zero static keys in pipeline — authentication via OIDC Workload Identity Federation. Application secrets in AWS Secrets Manager with automatic rotation every 30 days. Audit: no keys in CI environment variables (TruffleHog verification — report in Appendix B). | ✓ Yes |
| Does code go through automated security testing before deployment? | SAST + SCA + IaC scan | Required status checks blocking merge: (1) CodeQL SAST — static code analysis, (2) Trivy SCA — dependency and Docker image scanning, (3) Checkov — Terraform verification. No PR can be merged with an active CRITICAL alarm. Logs from the last 30 builds in Appendix C. | ✓ Yes |
| What is your policy for managing vulnerabilities in open source libraries? | Dependabot + SLA remediation | Dependabot auto-PR for every library with a CVE. SLA: CRITICAL ≤24h, HIGH ≤7 days, MEDIUM ≤30 days. Metrics from the last 90 days: 12 CRITICAL, avg fix time 11h. SBOM exported on every release. | ✓ Yes |
| Do you have a documented change management process for production code? | Git flow + change log | Every change via Pull Request with min. 2 reviewers (1 from security team). Direct push to main blocked (branch protection). Required: Jira ticket link, change description, test coverage ≥80%. Change log export for any period — available on auditor request. | ✓ Yes |
| Czy wykonujecie regularne testy penetracyjne lub security assessments? | Snapshot + roczny pentest | CI/CD Security Snapshot: quarterly pipeline configuration audit. External application pentest: planned periodically by client team. Results of the last Snapshot: 0 critical, 2 high (fixed), report available under NDA. | ~ partial |
| Describe your security incident response procedure in the production environment. | Incident Response Runbook | IR Runbook available in Confluence (version 2.1). Escalation: dev → senior → CISO → board. Client notification time: <4h from detection. Procedure tests: 2x per year... | ✓ Yes |
If your pipeline looks like what you saw above — it is worth a conversation.