Your Secure Coding Standard Isn’t a PDF — It’s a Set of Failing Checks

Stop asking developers to “remember compliance.” Bake policy into PRs, CI, and deploy pipelines so the secure path is the fast path—and you can produce audit evidence without a hero sprint.

If your secure coding standard can’t fail a build, it’s not a standard—it’s a suggestion.
Back to all posts

The day the auditor asked for proof (and we had vibes)

I’ve watched this movie more times than I care to admit: someone drops a 40-page “Secure Coding Standard” in Confluence, security feels good for a quarter, and then a real deadline hits. The next release includes a hardcoded API key, a permissive S3 bucket, and a "temporary" admin endpoint that somehow ships to prod.

The best part is always the audit. The auditor doesn’t want your PDF. They want evidence: what controls ran, when they ran, what they found, who approved exceptions, and whether the deployed artifact is the one that passed.

If your standards aren’t expressed as guardrails, checks, and automated proofs, you don’t have a standard—you have a wish.

Translate policy into developer moments (guardrails → checks → proofs)

Here’s what actually works: take every policy statement and rewrite it in a way that can be enforced where code changes happen.

  • Guardrails: defaults that make the safe path the easiest path (templates, scaffolds, secure libraries)
  • Checks: automated verifications that block bad merges/deploys (SAST/SCA/IaC policies)
  • Proofs: artifacts and logs you can hand to an auditor without a fire drill (SBOMs, signed builds, provenance)

A quick translation table I use with teams:

  • “Secrets must not be committed” → guardrail: pre-commit secret scan; check: CI secret scanning required; proof: CI logs + incident ticket for exceptions
  • “PII must be encrypted at rest and in transit” → guardrail: database module enforces TLS + KMS; check: Terraform policy denies non-encrypted resources; proof: IaC plan checks + KMS key policy evidence
  • “Changes must be reviewed” → guardrail: PR template + CODEOWNERS; check: branch protection requires reviews; proof: PR audit trail + required checks history

This is the mindset shift: compliance is a product requirement. It needs acceptance criteria, automation, and telemetry.

Put guardrails in the repo (so devs don’t have to memorize security)

If you rely on tribal knowledge, you’ll get tribal outcomes. Start with repo-level guardrails that are boring, consistent, and hard to bypass.

  • PR templates that force the right questions early (data touched, auth changes, logging changes)
  • CODEOWNERS so sensitive paths always get the right eyes
  • pre-commit hooks for fast feedback before CI
  • Baseline linting for common foot-guns (eslint, ruff, gosec, cargo-audit)

Example CODEOWNERS that reflects reality (regulated data needs owners):

# CODEOWNERS

# Anything touching auth needs the auth/security folks
/src/auth/                 @platform-auth @security

# Anything that touches PII/PCI/HIPAA flows gets privacy + data platform review
/src/pii/                  @privacy @data-platform

# Terraform changes require infra owners
/infra/terraform/          @sre @cloud-infra

Example pre-commit setup that catches the “oops” class of mistakes early:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.6.0
    hooks:
      - id: end-of-file-fixer
      - id: trailing-whitespace
      - id: check-yaml

  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.5.0
    hooks:
      - id: detect-secrets
        args: ["--baseline", ".secrets.baseline"]

  - repo: https://github.com/semgrep/semgrep
    rev: v1.74.0
    hooks:
      - id: semgrep
        args: ["--config", "p/owasp-top-ten"]

This isn’t about catching everything locally. It’s about killing the top 20% of issues that cause 80% of the embarrassment.

Turn standards into required CI checks (no check, no merge)

I’ve seen “security best practices” die the moment they become optional. The fix is uncomfortable but effective: branch protection with required status checks.

A practical baseline for most teams:

  • SAST: CodeQL (GitHub) or Semgrep rules tuned to your stack
  • SCA: dependency scanning (Dependabot, Snyk, OWASP Dependency-Check)
  • Secret scanning: run in CI even if you do pre-commit
  • IaC policy: OPA/Conftest for Terraform and Kubernetes YAML
  • Container scanning: Trivy for base image CVEs

Here’s a GitHub Actions example that’s opinionated but shippable:

# .github/workflows/security-gates.yml
name: security-gates
on:
  pull_request:

jobs:
  semgrep:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: semgrep/semgrep-action@v1
        with:
          config: >-
            p/owasp-top-ten
            p/r2c-security-audit

  dependency-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Trivy filesystem scan
        uses: aquasecurity/trivy-action@0.24.0
        with:
          scan-type: fs
          severity: CRITICAL,HIGH
          exit-code: '1'

  iac-policy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install conftest
        run: |
          curl -L https://github.com/open-policy-agent/conftest/releases/download/v0.55.0/conftest_0.55.0_Linux_x86_64.tar.gz \
            | tar xz
          sudo mv conftest /usr/local/bin/
      - name: Conftest against Terraform plan JSON
        run: |
          terraform -chdir=infra/terraform init
          terraform -chdir=infra/terraform plan -out=tfplan
          terraform -chdir=infra/terraform show -json tfplan > tfplan.json
          conftest test tfplan.json -p policy/terraform

A minimal Conftest policy to deny public S3 buckets (you’d be shocked how often this saves people):

# policy/terraform/s3.rego
package terraform.security

deny[msg] {
  some r
  r := input.resource_changes[_]
  r.type == "aws_s3_bucket_public_access_block"
  after := r.change.after

  after.block_public_acls == false
  msg := sprintf("S3 bucket public ACLs must be blocked: %s", [r.name])
}

Make these checks required. Otherwise you’ll get the Friday-afternoon “merge now, fix later” special.

Automated proofs: give auditors artifacts, not explanations

Regulated environments (SOC 2, PCI DSS, HIPAA, ISO 27001) don’t just want controls—they want repeatable evidence. The easiest way to get there is to generate proof as a byproduct of CI/CD.

What I like to ship by default:

  • SBOM (Software Bill of Materials) for every build (syft)
  • Signed artifacts (cosign) so you can prove what was deployed
  • Provenance/attestations (SLSA-ish) that link source → CI run → artifact
  • Retention: store outputs for a sane period (often 1–7 years depending on your world)

Example: generate an SBOM and sign an image in CI:

# Generate SBOM
syft packages dir:. -o spdx-json > sbom.spdx.json

# Build + sign container image (keyless signing)
docker build -t ghcr.io/acme/payments:${GITHUB_SHA} .
docker push ghcr.io/acme/payments:${GITHUB_SHA}

cosign sign --yes ghcr.io/acme/payments:${GITHUB_SHA}
cosign attest --yes --predicate sbom.spdx.json --type spdxjson ghcr.io/acme/payments:${GITHUB_SHA}

Now when the auditor asks “how do you ensure what you scanned is what you deployed?”, you can answer with:

  • a signed image digest
  • an attestation referencing the SBOM
  • CI logs showing required checks passed

That’s the difference between an audit that takes two days and an audit that eats a quarter.

Regulated data without killing delivery speed (design the lanes)

The teams that suffer most are the ones who treat regulated data as a vibe: sometimes prod data shows up in a dev laptop, sometimes not, and everyone is afraid to touch anything.

Here’s what actually reduces risk and keeps velocity:

  • Data classification in code and infra: tag resources and enforce policies based on tags (data_class=pii, pci_scope=true)
  • Separate data lanes:
    • dev/test uses synthetic or tokenized datasets
    • staging uses masked production snapshots (with approvals and access logging)
    • production is production (tightest access, shortest credential lifetime)
  • Short-lived access: SSO + OIDC to cloud roles, no long-lived IAM keys
  • Central secrets management: AWS Secrets Manager, GCP Secret Manager, Vault—and ban secrets in env vars where possible
  • Deterministic logging rules: never log raw PII; log identifiers and correlation IDs

Concrete example: enforce “no raw PII in logs” with a Semgrep rule tuned to your logger wrapper:

# semgrep/no-raw-email-in-logs.yml
rules:
  - id: no-raw-email-in-logs
    message: "Do not log raw email addresses; log a user_id or hash instead."
    severity: ERROR
    languages: [typescript]
    patterns:
      - pattern-either:
          - pattern: logger.$LEVEL(..., $EMAIL, ...)
          - pattern: logger.$LEVEL($EMAIL)
    metavariable-regex:
      metavariable: $EMAIL
      regex: ".+@.+\\..+"

Is it perfect? No. Does it stop the most common “oops we logged emails in debug” incident? Yes.

The real trick is not “more rules.” It’s clear boundaries: where regulated data may exist, who can access it, and how you prove it.

Exceptions and “break glass” paths (because reality exists)

If you make security gates absolute with no escape hatch, teams will route around you. I’ve seen people zip up code and email it because the pipeline was “too strict.” Congratulations, you just invented Shadow CI.

Instead, make exceptions explicit and measurable:

  1. Time-boxed waivers (e.g., 7–30 days) with auto-expiry
  2. Named owner (not “team”, a human) and a ticket reference
  3. Scope (which rule, which repo/path, which environment)
  4. Compensating controls documented (feature flag off, network isolation, extra monitoring)

A simple pattern: allow Semgrep ignores only with a justification comment, and fail CI if the comment doesn’t match a required format.

Also track two leadership-grade metrics:

  • Mean time to remediate (MTTR) for HIGH/CRITICAL findings
  • False positive rate for security checks (if it’s >20–30%, engineers will stop trusting it)

This is where GitPlumbers gets called in most often: after a year of “tool sprawl,” when CI is red all the time and everyone’s ignoring it. The fix is usually ruthless pruning, tuning rules to the codebase, and making evidence generation automatic.

What we do at GitPlumbers (and what to do Monday morning)

At GitPlumbers, we don’t sell silver bullets. We take your existing standards (or the half-finished ones), map them to guardrails/checks/proofs, and wire them into the workflows your teams already use—GitHub Actions, GitLab CI, ArgoCD, Terraform, Kubernetes, whatever’s actually running.

If you want a practical starting point for Monday:

  • Pick three non-negotiables (secrets, dependency risk, IaC misconfig) and make them required checks
  • Add SBOM + signed artifacts for one critical service
  • Establish data lanes (synthetic/tokenized dev, masked staging, locked-down prod)
  • Create an exception workflow with expiry and owners

You’ll be shocked how quickly “compliance” stops being a quarterly panic and becomes just… the way you ship.

Related Resources

Key takeaways

  • A “secure coding standard” that isn’t enforced by **automated checks** will be ignored under pressure—every time.
  • Translate policy into three layers: **guardrails** (safe defaults), **checks** (gates in CI), and **proofs** (attestations, logs, and artifacts an auditor will accept).
  • Put security requirements directly into the developer workflow: `pre-commit`, PR templates, required status checks, and `CODEOWNERS`.
  • Make regulated-data constraints survivable by separating **data zones**, using **tokenization/masking**, and requiring **short-lived credentials**—not by banning productivity tools.
  • Treat exceptions as a first-class workflow: time-boxed risk acceptance, measurable blast radius, and auto-expiring waivers.

Implementation checklist

  • Inventory your top 10 security/compliance requirements and map each to: guardrail, check, proof
  • Add repo-level guardrails: PR template, `CODEOWNERS`, baseline linters, `pre-commit`
  • Add CI gates: SAST + SCA + secret scanning + IaC policy checks
  • Generate proofs: SBOM, signed artifacts, provenance/attestations, retention policy
  • Define regulated-data rules: classification, approved stores, encryption, access patterns
  • Create an exception workflow with owners, expiry, and audit trail
  • Track operational KPIs: % PRs blocked, false positive rate, MTTR for security findings

Questions we hear from teams

How do we avoid drowning engineers in false positives from SAST tools?
Start with a small, high-signal ruleset (OWASP Top 10 class issues), tune it to your frameworks, and measure false positives explicitly. Make rule changes a normal PR-driven workflow, not a quarterly security-only event. If a rule is noisy, downgrade severity or narrow patterns—don’t train the org to ignore red builds.
What’s the minimum “automated proof” set that auditors actually accept?
A reliable baseline is: CI logs for required checks, immutable artifact digests, an SBOM for the artifact, and a signing/provenance story that links source commit to the built artifact (e.g., `cosign` signatures + attestations). Retain these artifacts and logs per your compliance retention requirements.
How do we balance regulated data constraints with developer productivity?
Create data lanes: synthetic/tokenized data for dev, masked snapshots for staging with approvals, and strict controls for prod. Enforce short-lived access via SSO/OIDC, centralize secrets, and encode “no raw PII in logs” and “encrypt by default” into templates and automated checks.
We’re getting a lot of AI-generated code in PRs—what changes?
Treat it like any other code, but assume it’s more likely to include insecure defaults, outdated patterns, and license surprises. Tighten SCA/SAST gates, require SBOMs, and add targeted rules for auth/logging/crypto. GitPlumbers often sees “vibe-coded” changes bypass reviews because they look confident—automation is how you keep confidence from becoming risk.

Ready to modernize your codebase?

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

Get a guardrails/checks/proofs map for your org See how we fix brittle CI and noisy security gates

Related resources