Skip to content
AI|Course
← Analyze AI-Generated Code for Quality and Security

Lesson 5 of 7 · 2 hr

Automated analysis and governance

Human review does not scale to AI-generated volume, and it misses things people are bad at spotting. Automate the checks that can be automated, in layers, so each one catches what the others do not.

Watch

A security vendor's explanation of static application security testing (SAST) and how it finds problems in code without running it.

Watch on YouTube (opens in a new tab)Video by Aikido Security. The player loads only when you press play.

The layered pipeline

LayerCatchesExamples
Formatters and lintersStyle, obvious bugs, unused code, risky constructsRuff and ESLint
Type checkersWrong types, missing attributes, invented membersmypy, TypeScript
Static application security testing (SAST)Injection, unsafe functions, insecure patternsCodeQL, Semgrep, Bandit
Secret scanningKeys, tokens and passwords in code and historyGitleaks
Dependency and license scanningKnown vulnerabilities and license issuesOSV-Scanner, Dependabot
TestsBehavior, regressionsUnit, integration, property tests (lesson 6)
Human reviewDesign, intent, authorization logic, anything subtleYou

Many hallucinated function calls and wrong parameter types are caught for free by a type checker, so turn one on, even in an existing untyped codebase, starting with the files tools touch most.

SAST: how it works and how to use it

Static analysis reads code without running it, following how data flows from sources (user input) to sinks (database queries, shell commands) to find unsafe paths.

  • CodeQL treats code as a database you query, with a large library of security queries. It integrates with GitHub code scanning.
  • Semgrep matches code patterns with rules that are easy to write, so you can encode your own team's rules.
  • Bandit is a Python-specific security linter.

A custom Semgrep rule that bans shell=True:

rules:
  - id: no-shell-true
    languages: [python]
    severity: ERROR
    message: Do not pass shell=True to subprocess. Pass an argument list instead.
    pattern: subprocess.$FUNC(..., shell=True, ...)

Turn every recurring review comment into a rule like this. That way each lesson learned from AI-generated mistakes protects the whole codebase permanently.

Secret scanning

Run a scanner such as Gitleaks in pre-commit and in CI. If a secret is committed, assume it is compromised: revoke and rotate it, then clean up. Deleting the line is not enough because git history keeps it.

Handling noise

Scanners produce false positives, and developers learn to ignore noisy tools.

  • Start with a small, high-confidence rule set, and expand gradually.
  • Triage and suppress with a written reason and an expiry date, never silently.
  • Block merges only on high-severity, high-confidence findings. Report the rest.
  • Measure time to fix and recurring finding types, and feed these back into training and rules.

Using an LLM as a reviewer

Language models can review a diff and catch issues static tools miss, or explain a finding and propose a fix. Research on AI-generated code has also shown that a chat assistant, given static analysis warnings, could fix a good share of them. Use models this way with care:

  • Treat their output as suggestions to verify, not verdicts. They produce false positives and misses.
  • Give them context: the threat model, the project rules and the surrounding code.
  • Do not make a model's approval your only gate. A model that wrote the code and the model that reviews it may share the same blind spots.
  • Re-run the real tools and tests after any model-suggested fix.

Governance for AI-assisted development

  • A written policy on which tools are approved, what data may be sent to them and what must be reviewed.
  • Pull request templates that ask: was AI used, which parts, what was verified.
  • Training for developers on the failure patterns in these lessons.
  • Standards alignment. NIST's Secure Software Development Framework (SSDF) describes secure development practices, and NIST publishes a community profile of it for generative AI. CISA's Secure by Design materials describe how vendors can build security into products. Use them to structure your program and show auditors what you do.
Make the safe path the easy path

Put these checks in a pre-commit hook and a required CI job, with fast feedback. Developers and agents then find out about a problem in seconds, not at release time.

Resources

DocsGitHub Docs
About code scanning (opens in a new tab)

How CodeQL-based scanning runs in pull requests.

DocsGitHub
CodeQL documentation (opens in a new tab)

Query language and library reference.

DocsSemgrep
Semgrep documentation (opens in a new tab)

Writing custom rules and running scans in CI.

CodeGitHub
Gitleaks (opens in a new tab)

A secret scanner for repositories and history.

DocsAstral
Ruff (opens in a new tab)

A fast Python linter and formatter.

DocsESLint
ESLint (opens in a new tab)

The standard JavaScript and TypeScript linter.

Docsmypy
mypy (opens in a new tab)

A static type checker for Python.

DocsNIST
NIST SP 800-218: Secure Software Development Framework (opens in a new tab)

A framework of secure development practices.

DocsNIST
NIST SP 800-218A: SSDF Community Profile for Generative AI (opens in a new tab)

The profile covering secure development practices for generative AI and foundation models.

DocsCISA
Secure by Design (opens in a new tab)

Principles for building security into software products.

Check yourself

  1. Add a linter, a type checker, one SAST tool and a secret scanner to a project, running in CI.
  2. Introduce three deliberate flaws (SQL injection, shell=True, a fake API key). Confirm each layer catches what you expect.
  3. Write two custom Semgrep rules from mistakes you found in lessons 2 and 3.

Found a broken link or a mistake? Report it (opens in a new tab).