Air traffic control for AI coding

How to Stop AI Coding Agents From Committing Secrets

Refuse secrets before they land, with a rule that gives the same verdict every time, instead of finding them in CI after the commit.

  • Configurable YAML rules beside the code.
  • Hard WASM rules installed by your platform team.
  • Task rails and named approval for sensitive paths.

An AI coding agent will paste a credential into source code for the same reason a tired engineer does: it was in the context, and the task said "make it work". The difference is volume. One person does it once a year. A fleet of agents has a hundred chances a day.

Most teams catch secrets after the fact: a scanner in CI, a push-protection rule on the forge, an alert from the cloud provider. All three fire after the key is already in a commit. This post shows how to stop it one step earlier, before the change lands, with a rule that gives the same answer every time.

Why review is not enough

An AI reviewer can catch a hard-coded key. It can also miss it tomorrow, because a model's verdict depends on the prompt, the context and the sampling. A human reviewer catches it when they are not reviewing their fortieth agent change of the day.

A secret is a case where you do not want judgement. You want a rule.

A configurable rule, written in YAML

In Nool, a simple rule is a YAML file that lives beside your code. This is the rule from a real run on nool 7.24.0, in a fresh repository:

inv_id: POL-WS-001
title: "No secret keys in source code"
scope: { type: global }
kind: behavioral
severity: critical
mode: blocking
reason_code: 403
when:
  # skip tests: fixtures may hold fakes
  not: { semantic_path_is_test: true }
forbids:
  # refuse AWS access key IDs
  - diff_matches: "AKIA[0-9A-Z]{16}"

When an agent proposed a change to src/config.ts containing an AWS access key ID, Nool refused it before it landed:

[Aram DSL] POL-WS-001 failed: diff_matches(AKIA[0-9A-Z]{16})
Error: Gate stack denied [POLICY_DENY] at stage pre-propose: reason code 403

Run it again and you get the identical verdict. The same fake key inside a test fixture landed, because the rule says fixtures may hold fakes. Edit the file and the new rule applies to the next change, and the edit itself has to pass the same checks.

A hard rule your agents cannot edit

Some rules must not bend, even for an agent that has write access to the repository. For those, your platform team writes the logic in code, compiles it to WASM and installs it. It runs sealed off: no network, no files, no clock. Changing it means rebuilding and reinstalling it, not editing a file an agent can reach.

This one, from the same run, refuses billing code that arrives without a test:

fn evaluate(ctx: &PolicyContext) -> PolicyResult {
  let changed = changed_paths(ctx);
  let has_test = changed.iter().any(|p| p.ends_with(".test.ts"));
  for p in &changed {
    if p.starts_with("src/billing/") && !has_test {
      return PolicyResult::deny(499, format!(
        "{p} is billing code: include a test for it in the same change"));
    }
  }
  PolicyResult::Allow
}

The same pattern covers secrets you cannot express as a regular expression: tokens that need an entropy check, or a list of internal hostnames your platform team maintains.

Keep agents out of sensitive code entirely

The cheapest secret leak is the one in code the agent never touches. A Nool task can name the paths its work must not touch. In this run an agent raising a quota also edited the authentication crate, and the change was refused:

Error: TASK_RAILS refused: task rails: this change touches paths its task declared off-limits:
  - crates/auth/src/lib.rs (rail crates/auth) of task e4389486 'Raise the Community quota to 2,000 requests'

And where an agent is allowed to touch sensitive code, you can require a named person to sign off. A change to crates/auth waits for the security lead:

⏸ Approval request 1062415c… (pre-solidify): pending: needs 1 × steer:ciso

What you get

  • Secrets refused before they land, not found after.
  • The same verdict on every run, because no model makes the call.
  • Two kinds of rule: configurable rules your team edits, and hard rules your platform team installs.
  • Every verdict kept with the change as audit evidence.

Read how the rules work in policy as code for coding agents, see the wider security and governance picture, or put Nool on your own repository and write your first rule.