Integrating Security Scanners into Your AI Coding Workflow

Integrating Security Scanners into Your AI Coding Workflow

pr0h0
securityai-codingdevsecopsstatic-analysis
AI Usage (86%)

I usually treat AI-generated code the same way I treat code from a rushed teammate: useful, fast, and worth scanning before it lands anywhere important. The difference is that AI can produce a lot of plausible-looking surface area very quickly, which makes it easier to miss dependency drift, insecure patterns, or a leaked secret in a diff that looked harmless at first glance.

Why AI-assisted coding changes the scanning problem

The problem shifts because the speed shifts. A human reviewer might add one library and a small helper. An AI pair programmer may add a new package, a config file, a route handler, and a test fixture in one pass. That creates more places for security bugs to hide.

The other change is confidence. AI often writes code that looks consistent with the rest of the project, so teams tend to trust it too early. I do not trust the shape of the code; I trust the checks around it.

What to scan first in an AI-generated change

Start with the failure modes that cause real damage.

Dependency and supply-chain checks

If the change introduces or updates packages, inspect the dependency tree before you inspect style. A surprising number of incidents start with a package that was added for convenience and never reviewed again.

Look for:

  • new runtime dependencies
  • postinstall scripts
  • packages with unusual ownership or low maintenance
  • lockfile churn that is larger than expected

A simple rule helps here: if the AI adds a package, the review must explain why that package is needed and whether the same result can be done with built-in APIs.

SAST and secret detection

Static analysis should run on every meaningful change, but the important part is what it flags in context. AI-generated code often repeats insecure templates:

  • string-built SQL
  • direct shell execution
  • unsafe innerHTML
  • auth checks in the frontend only
  • logging of request bodies and tokens

Secret scanners matter just as much. AI sometimes copies environment variable names into examples, debug statements, or fixture files. That is not always malicious, but it is still a leak if the pattern makes it to a shared branch.

Linting rules that catch risky patterns

Linting is not a security scanner by itself, but it catches the dumb mistakes that AI likes to repeat. Tighten rules around:

  • dangerous regexes
  • eval
  • raw HTML insertion
  • insecure fetch usage
  • unused async errors
  • weak equality or coercion in auth logic

If you already use ESLint, add security-relevant rules instead of relying on general style checks. Style fixes are cheap. Security regressions are not.

A practical JavaScript workflow that runs scans on every change

I prefer a layered setup: fast local checks first, deeper checks in CI.

Pre-commit checks for fast feedback

Pre-commit should be quick enough that nobody disables it. Keep it to the checks that catch obvious issues early:

  1. format and lint the changed files
  2. run secret detection on the staged diff
  3. run a lightweight dependency audit if package files changed

A minimal hook might look like this:

pre-commit.sh
#!/usr/bin/env bash
set -euo pipefail

files="$(git diff --cached --name-only --diff-filter=ACM)"

echo "$files" | grep -E '(package-lock.json|pnpm-lock.yaml|yarn.lock|package.json)' >/dev/null && npm audit --audit-level=high || true
echo "$files" | xargs npx eslint
echo "$files" | xargs npx secretlint || true

I would not make every local scan blocking if the tool is flaky, but I would make the warnings impossible to ignore.

CI checks for full coverage

CI is where you enforce the harder gate:

  • dependency audit
  • SAST
  • secret scan
  • tests
  • policy check for risky files

That policy check matters when AI adds files that humans forget to inspect, like a new helper under scripts/ or an endpoint under api/. The scan should not care how the file got there. It should care what it does.

LayerWhat to catchWhy it matters
Pre-commitobvious secrets, risky syntax, new dependenciesfast feedback for the author
CIbroader SAST and auditsconsistent enforcement
Reviewlogic flaws and bad assumptionsscanners miss intent

Failing safely when a scanner is noisy or unavailable

Do not turn scanner outages into silent approval. If a scan fails because the tool is down, I prefer one of two behaviors:

  • block merges until the check is rerun
  • allow only with explicit manual approval and a follow-up ticket

What you should avoid is a fallback that quietly skips the gate. That is how temporary tooling problems become permanent process holes.

How to reduce false positives without weakening the gate

False positives are real, especially with generated code that reuses templates or test data. The fix is not to loosen everything. The fix is to tune narrowly.

A few practical habits help:

  • baseline existing findings before enabling a new scanner
  • suppress only with file-level or line-level justification
  • review suppressions monthly
  • separate “known safe pattern” from “annoying warning”

If a scanner complains about a pattern you use intentionally, codify the safe version in a shared helper or lint rule. Do not teach the team to click through alerts.

What scanners miss and how to add human review

Scanners are good at syntax and pattern matching. They are weak at business logic. AI-assisted code often fails in subtle ways that no generic tool can prove:

  • authorization logic in the wrong layer
  • trusting client-provided role flags
  • exposing internal actions through a new endpoint
  • mixing test-only behavior into production paths

That is why review still matters. I ask one question on AI-generated changes: “What trust boundary moved here?” If nobody can answer that clearly, the code needs a second pass.

Example report triage with a safe code snippet

Here is a case I like to use when triaging findings. The scanner flags a suspicious string concatenation:

search.js
export function buildQuery(term) {
const clean = String(term).trim().slice(0, 50);
return {
  text: "SELECT id, name FROM users WHERE name ILIKE $1",
  values: [`%${clean}%`],
};
}

The scanner may still complain because it sees query construction. Human review should confirm that parameterization is preserved and that the input is bounded. If this were raw string interpolation into SQL, the answer would be different.

The useful part of the report is not the alert alone. It is the decision: safe because the query uses a placeholder, or unsafe because the code actually builds SQL text directly.

Conclusion

AI coding tools do not remove the need for scanning. They increase the value of a good scan pipeline because they make it easier to introduce breadth without thinking through the edges.

My rule is simple: scan early, scan again in CI, and keep a human in the loop for any change that touches trust, data handling, or dependencies. If the code came from an AI, that is exactly the time to be boring and systematic.

Share this post

More posts

Comments