Stop Writing Policy PDFs—Ship Guardrails in Code

If your security policy lives in Confluence, it’s already out of date. Translate rules into automated checks, proofs, and exceptions that fit the way developers actually work.

Policies that don’t execute in the pipeline are just opinions. Ship guardrails, not guidance.
Back to all posts

The policy PDF that broke the release

I’ve watched well-meaning teams roll out 40-page “secure coding standards” only to watch developers click past them like EULAs. A fintech I worked with pushed a last-minute PCI DSS update into Confluence on a Friday. Monday’s release died because the only engineer who read it added manual steps no one else did. Two hours of finger-pointing later, we turned the policy into a pre-merge check and the drama vanished.

If the rule isn’t executable where devs work—IDE, pre-commit, CI, and runtime—it’s not a standard. It’s a suggestion.

Translate policy to guardrails developers can feel

Start by mapping human-language policy to concrete checks. Keep it tight—8–12 controls that actually reduce risk.

  • Secrets management:
    • pre-commit with detect-secrets to stop API keys before they hit Git.
    • CI fails if secrets found in diff; require remediation or redact + rotate.
  • Dependency risk (SCA):
    • Use npm audit/yarn audit, pip-audit, or mvn versions + OWASP Dependency-Check.
    • Enforce org-wide blocklist (e.g., left-pad-style abandoned libs) via Renovate rules.
  • SAST rules:
    • semgrep fast rules in the IDE and pre-commit for taint sinks, unsafe deserialization, and SSRF.
    • Level up to deep SAST in CI nightly to keep pipelines fast.
  • Infrastructure-as-Code:
    • Terraform: Checkov or tfsec to catch public S3 buckets, open SGs, and unencrypted EBS.
    • Kubernetes: kubeconform + kubesec for baseline; Kyverno or Gatekeeper at admission.
  • Container images:
    • Trivy or Grype in CI; fail on critical CVEs without fixes, warn otherwise with SLA.
  • Code review and ownership:
    • CODEOWNERS for security-sensitive dirs; enforce 2 reviewers for auth and crypto.
  • Logging and PII:
    • CI/IDE regex + pii-detector rules to block plaintext PII in logs; force structured logging.
  • Data egress and DLP:
    • Stop uploads of prod data to public buckets with organization policies and OPA checks.

The policy lives where developers live: the PR view, not a wiki.

Wire it into the workflow: IDE → pre-commit → CI → runtime

Make the happy path fast and the unsafe path impossible.

  1. IDE extensions
    • Semgrep, ESLint, Bandit, Hadolint. Quick feedback < 500ms.
  2. Pre-commit hooks
    • pre-commit config with detect-secrets, semgrep --config p/owasp-top-ten, black/eslint.
  3. CI gates (fast path)
    • Parallel jobs under a 5–7 min budget: Trivy image scan, Checkov IaC, Semgrep focused rules.
    • Fail only on: new hardcoded secrets, critical vulns with fixes, critical IaC misconfigs.
  4. Nightly/weekly jobs (deep path)
    • Full SAST/DAST, license audits, and SBOM diffs. Open tickets, don’t block builds.
  5. Registry and admission
    • Push to an internal registry (Artifactory/Nexus/GHCR). Block deploys lacking signed attestations.
    • Cluster enforces Kyverno/Gatekeeper policies: no :latest, runAsNonRoot, memory/CPU limits.

The developer experience rule: fast feedback locally, lightweight blocking in CI, hard stops at runtime for anything that slipped through.

Prove it: automated evidence, not screenshots

Auditors don’t want screenshots; they want repeatable proofs. Treat evidence as artifacts.

  • SBOMs: Generate with syft in CI. Store alongside images. Track diffs across releases.
  • Attestations: Use cosign + in-toto to sign build provenance (builder, commit, timestamp), SCA report, and policy evaluation. Adopt SLSA levels as your maturity target.
  • Policy-as-code: Write Rego for Conftest/Gatekeeper to encode rules with rationale and exceptions.
  • Evidence store: Artifact repository bucket or rekor/transparency log. Link evidence to the release tag.
  • Auditable pipeline: PR → CI run → attestations → deployment stamp. One link per release with proofs.

Example Rego to block public S3 buckets:

package terraform.s3

violation["S3 bucket must not be public"] {
  resource := input.resource.aws_s3_bucket[_]
  resource.acl == "public-read"
}

And a minimal GitHub Actions step:

- name: IaC Policy Check
  run: |
    terraform plan -out tf.plan
    terraform show -json tf.plan > tf.json
    conftest test tf.json --policy policy/terraform

For attestations:

- name: Generate SBOM and Sign
  run: |
    syft dir:. -o spdx-json > sbom.json
    cosign attest --predicate sbom.json --type spdx \
      --key $COSIGN_KEY $IMAGE_REF

Keep regulated data safe without killing velocity

You can ship fast and still respect PCI DSS, HIPAA, SOC 2, GDPR. The trick is data discipline and separation.

  • Data classification at the code level:
    • Tag models/DTOs with @pii(email)/@phi annotations; lint on unsafe sinks.
    • Block logging of tagged fields via custom eslint/semgrep rules.
  • No prod data in dev:
    • Synthetic datasets with Gretel/Tonic.ai/Synth. Seal this with a policy check in CI that rejects imports of live dumps.
  • Access via brokers, not creds:
    • Use short-lived tokens (Vault dynamic DB creds) and rotate automatically.
  • Crypto defaults:
    • AWS KMS/GCP KMS + Vault Transit for encryption; enforce TLS and at-rest by policy.
  • DLP in CI and logs:
    • Scan build logs and artifacts for PII patterns. Fail builds that leak customer data.
  • Runtime guards:
    • Service mesh (Istio) to enforce mTLS and policy; network policies default-deny. Feature flags (LaunchDarkly) to decouple risky rollouts.

Balance comes from a two-lane model: fast lane for low-risk changes with automated proofs; slow lane with extra checks for code touching regulated data. Developers pick the lane in the PR template and policy validates it.

Tooling that actually works together

I’ve seen teams drown in scanners. Pick a thin, composable stack and standardize.

  • Local: pre-commit, detect-secrets, semgrep, eslint, bandit.
  • CI: Trivy (images), Checkov/tfsec (IaC), Semgrep (app), Syft (SBOM), Cosign (signing).
  • Control plane: OPA + Conftest for CI; Kyverno/Gatekeeper for clusters.
  • Dependencies: Renovate bot with security priority lanes and auto-merge for patch-level updates.
  • Artifact + evidence: GHCR/Nexus/Artifactory; rekor or signed attestations stored next to images.
  • Dashboards: DefectDojo or a lightweight Data Studio/Looker dashboard for MTTR, exceptions, build SLA.

Keep versions pinned and reproducible (containerized tools). Bake these into a reusable CI template for GitHub Actions or GitLab CI so every repo gets the same baseline in one PR.

Make it stick: ownership, metrics, and sane exceptions

Security programs fail on process, not tools. Set clear ownership and feedback loops.

  • RACI: App teams own fixing; platform owns the guardrails; security writes policies as code and reviews exceptions.
  • Metrics that matter:
    • Pipeline P50 and P95 duration with a hard budget (e.g., 7 min P95 for fast path).
    • Vulnerability MTTR by severity; SLOs tied to breach risk, not vanity counts.
    • Exception TTLs (e.g., 14 days for critical CVEs) and auto-expiry with reminders.
    • Change failure rate (DORA) to ensure security didn’t tank reliability.
  • Exception workflow:
    • PR label security-exception requires risk rationale, compensating controls, and expiry date.
    • Auto-generated ticket with evidence links; Slack bot pings owners 48 hours before expiry.

A little ceremony, a lot of automation, and no surprises during audits.

A minimal reference you can copy this week

Drop-in bits for a pilot repo:

  • CODEOWNERS

    # Sensitive areas require security review
    app/auth/ @security-team @backend-leads
    infra/ @platform-team
  • .pre-commit-config.yaml

    repos:
      - repo: https://github.com/Yelp/detect-secrets
        rev: v1.4.0
        hooks:
          - id: detect-secrets
      - repo: https://github.com/returntocorp/semgrep
        rev: v1.80.0
        hooks:
          - id: semgrep
            args: ["--config", "p/owasp-top-ten", "--error"]
  • GitHub Actions (excerpt)

    name: secure-ci
    on: [pull_request]
    jobs:
      fast-checks:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - run: pip install checkov trivy-cli conftest syft cosign semgrep
          - run: semgrep ci --config p/owasp-top-ten
          - run: checkov -d infra/
          - run: trivy fs --exit-code 1 --severity CRITICAL,HIGH .
          - run: syft dir:. -o spdx-json > sbom.json
          - run: cosign attest --predicate sbom.json --type spdx --key $COSIGN_KEY $IMAGE

Pilot it in one service that ships weekly. Measure the pain for two sprints, tune thresholds, then templatize across repos.

Related Resources

Key takeaways

  • Turn policy statements into executable checks in the IDE, pre-commit, CI, and runtime.
  • Automate proofs: SBOMs, vulnerability scans, and signed attestations stored with artifacts.
  • Protect regulated data with classification, synthetic data, and strict secrets management.
  • Measure friction and risk together: pipeline SLA, MTTR on vulns, exception TTLs.
  • Start minimal: one repo, a small ruleset, and a visible exceptions process—iterate weekly.

Implementation checklist

  • Define 8–12 policy controls that actually matter (secrets, dependencies, IaC, images, PII).
  • Add developer-local checks: `pre-commit` with `detect-secrets` + `semgrep` quick rules.
  • Enforce CI gates with `Conftest`, `Trivy`, `Checkov`, and dependency scanning.
  • Generate SBOM (`syft`) and sign build artifacts + attestations (`cosign`, `in-toto`).
  • Block risky deploys with cluster admission policies (`Kyverno`/`Gatekeeper`).
  • Centralize evidence in artifact storage; auto-link to tickets and PRs.
  • Set metrics: build time budget, vuln MTTR, exception TTL, change failure rate.
  • Pilot in one service, document learnings, then templatize across repos.

Questions we hear from teams

How do we keep build times from exploding?
Keep fast-path checks under a 5–7 minute P95 budget by focusing on high-signal rules (secrets, critical CVEs with fixes, critical IaC misconfigs). Push heavy scans (deep SAST/DAST, full license audits) to nightly jobs that create tickets. Track pipeline time as an SLO and enforce it.
What about legacy monoliths with no tests and shared prod data?
Start with read-only SBOM and image scanning to understand risk. Add secrets scanning and a dependency bot (Renovate). Introduce a data access broker and synthetic datasets before touching pipelines. Carve out a golden path service as a pilot and backport what works.
We’re under HIPAA/PCI—do we need specialized tools?
You need disciplined evidence, not vendor logos. Use SBOMs (Syft), attestations (Cosign/in-toto), policy-as-code (OPA), and strict key/secret management (Vault/KMS). Map controls to your policy matrix and export audit bundles per release.
How do we handle false positives without killing morale?
Triage findings by severity and novelty (new vs existing). Allow PR-scoped suppressions with justification and TTL. Tune rules weekly, measure accepted suppressions, and remove noisy checks. Put security engineers on monthly office hours with teams to fix root causes.
Where do we store proofs for auditors?
Store signed attestations, SBOMs, scan reports, and policy evaluation results in your artifact repository alongside images. Link them to releases via Git tags and keep a simple dashboard that exports a zip with an audit bundle on demand.

Ready to modernize your codebase?

Let GitPlumbers help you transform AI-generated chaos into clean, scalable applications.

Talk to GitPlumbers about guardrails that actually ship Get our minimal secure CI template

Related resources