Skip to main content

AxiomGate Linter (axiomgate-lint)

Someone on your team wrote allow_delegation=True. CrewAI ships that switch off, so turning it on was a decision. This linter finds those decisions and puts them in front of a reviewer.

CI License: MIT Python 3.10+ OWASP Top 10 for LLMs


Why

A CrewAI agent with allow_delegation=True can hand work to other agents, which can hand it on again. There's no depth limit and no approval step. It's one keyword, easy to add while debugging and easy to forget, and it disappears in a diff full of prompt changes.

Here's the shape it usually takes. A researcher agent delegates drafting to a writer. The writer hits a gap and delegates the missing fact back to the researcher, which delegates part of it onward again. Nothing is broken and nothing raises an error. Every hop is a fresh model call against a real budget, and the first thing anyone notices is the invoice. The second is that no human approved any step after the first one.

That's the cheap version. The expensive version is the same loop with a tool attached that writes to something.

CrewAI defaults to allow_delegation=False (source). That's a sensible default, and it's why the explicit True is worth finding. Nobody got it by accident. Somebody typed it, and this linter asks whether anyone reviewed that.


What it does

axiomgate-lint reads Python with the standard library's ast module and reports what it finds, mapped to the OWASP Top 10 for LLM Applications.

No dependencies. No network calls. No API keys. Nothing leaves your machine, and it never executes the code it reads, so it's safe to point at a repository you haven't looked at yet.


What it catches

Rule Severity OWASP LLM Meaning
AG-DEL-001 HIGH LLM06: Excessive Agency An agent is built with an explicit allow_delegation=True, so it can delegate to other agents with no depth limit and no human approval

One rule so far. It's a real one, and the tool doesn't claim more than it checks.

The rule fires only when the file imports the framework. A class of your own that happens to be called Agent — a sales agent, an insurance broker — is not CrewAI's, and reporting it as LLM06 would be a false alarm about code that has nothing to do with LLMs.


What it misses

A security tool that hides its blind spots is worse than no tool, because people trust it. Here are the current ones.

  • Python files only. CrewAI's newer pattern puts agent config in agents.yaml. Delegation turned on there is invisible to this linter.
  • Literal values only. It catches allow_delegation=True and allow_delegation=1. It won't catch allow_delegation=cfg.delegate, a value read from the environment, or anything else whose value is only known once the code runs.
  • Configuration held in a variable. Agent(**{"allow_delegation": True}) is read, because the dict is right there. Agent(**config) is not: what config holds is not knowable without running the code, and assuming every **kwargs factory delegates would flag every factory in the project.
  • Names it can follow. It tracks delegation switched on afterwards through a variable, a second name for the same agent, an attribute of the method's own receiver (self.researcher), a constant index into a literal list, tuple or dict, a walrus, tuple unpacking, and setattr(a, "allow_delegation", True). It does not follow a computed index (agents[i]), a computed attribute name, a container filled in a loop or a comprehension, or an object built in another module.
  • A visible import is required. The rule needs from crewai import Agent (or import crewai) in the same file. A factory in that same file is followed; one in another module, a wildcard import, and dynamic construction are not. This is deliberate: without the import there is no evidence the class is the framework's, and claiming LLM06 about someone's sales agent is worse than saying nothing.
  • CrewAI's parameter only. allow_delegation is a CrewAI concept. AutoGen and OpenAI Swarm have no equivalent, so running this against their code finds nothing. That's a check that didn't apply, not a clean bill of health.
  • Source, not behaviour. It doesn't run your agents, watch them, or prove anything about permission boundaries.

Every one of these is a place where the honest answer is "not knowable without executing the code". The rule stays quiet there on purpose. A rule that guesses is worse than no rule.

Zero findings means this rule didn't match. It doesn't mean your system is safe.


Install

pip install axiomgate-lint

Or straight from the repository, which is the same code at whatever main currently is:

pip install git+https://github.com/robin-svensson/axiomgate-lint.git

Or clone it:

git clone https://github.com/robin-svensson/axiomgate-lint.git
cd axiomgate-lint
pip install .

Python 3.10 or newer, nothing else.

Check it works

A linter that silently does nothing looks exactly like a linter that found nothing. The repository ships two small files so you can tell them apart:

axiomgate-lint examples/vulnerable_crew.py   # one HIGH finding, exit 2
axiomgate-lint examples/safe_crew.py         # nothing,          exit 0

If the first one is quiet, something is wrong with the install, not with your code.


Use it

axiomgate-lint .

Nothing matched:

=== AxiomGate AI Agent Linter ===
Target: /path/to/your/project
Total findings: 0

Something matched:

=== AxiomGate AI Agent Linter ===
Target: /path/to/your/project
Total findings: 1

[HIGH] AG-DEL-001 at /path/to/your/project/src/crew.py:29
  Unattenuated agent delegation enabled (allow_delegation=True). Vulnerable to excessive agency loops. (LLM06: Excessive Agency)
  -> Set allow_delegation=False or add a human-in-the-loop gate before delegation.

Options

axiomgate-lint [target] [--fail-on critical|high|medium|low|any] [--format text|json|github] [--strict] [--version]
Flag Default Does
target . Directory or file to scan
--fail-on high Severity at or above which the run exits 2
--format text text to read, json for tooling, github for CI annotations
--strict off Exit 1 if any file could not be read or parsed

Exit codes

Code Meaning
0 Scanned, nothing at or above the threshold
1 Could not scan: the target doesn't exist, a flag was used wrongly, or --strict was set and a file could not be read while nothing was found
2 Scanned, found something at or above the threshold

Code 1 earns its place. axiomgate-lint ./scr is a typo, and a tool that answers "0 findings, exit 0" to a path it never opened hands you a green pipeline for a scan that never ran. Scanning nothing and finding nothing are different results and only one of them should pass.

Individual files that can't be parsed or read are reported as warnings and don't change the exit code. The count also appears in the summary, so "0 findings" is never the whole story when part of the tree went unread. Pass --strict to make that a failure instead.

A usage error — a mistyped flag — exits 1, not argparse's usual 2. In a pipeline, 2 means a finding, and a typo should not look like a vulnerability.

A finding outranks an unreadable file: with --strict, a run that finds something still exits 2. Downgrading a real finding to "could not scan" because an unrelated file had a syntax error would send it down a pipeline's retry branch instead of its security branch.


GitHub Actions

As a step in your own workflow:

name: AI Agent Security Lint

on: [push, pull_request]

jobs:
  lint-agents:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-python@v7
        with:
          python-version: '3.11'
      - run: pip install git+https://github.com/robin-svensson/axiomgate-lint.git
      - run: axiomgate-lint . --fail-on high --format github

Or use the bundled action:

      - uses: robin-svensson/axiomgate-lint@v0.2.2
        with:
          target-path: ./src
          fail-on: high

With --format github the findings show up as annotations on the changed lines. Anything that fails the build is emitted as ::error and the rest as ::warning, so what the reviewer sees matches what the pipeline decided.


Contributing

Issues and pull requests are welcome, especially new rules and especially evidence that an existing rule is wrong.

A new rule needs three things: a test that fails before the rule exists, a real pattern it matches, and a plain statement of what it can't see. A rule that guesses is worse than no rule.

False positives are the worst failure this tool has, and false negatives are security bugs, not feature requests — there's an issue template for each. Everyone taking part is expected to follow the code of conduct, and vulnerabilities go through SECURITY.md rather than the issue tracker.

git clone https://github.com/robin-svensson/axiomgate-lint.git
cd axiomgate-lint
pip install -e ".[dev]"
pytest

Where this comes from

axiomgate-lint is the open part of AxiomGate. The linter does one check, well and for free.

There is more to AxiomGate than the two public repositories here — a commercial auditor that maps agent topologies and capability boundaries and produces evidence-backed reports. That part is closed, so take nothing about it on this page as verified: you cannot read it, and a claim you cannot check is worth what you paid for it. What you can check is in this repository and in the kernel.

This linter finds unattenuated delegation in source. It cannot stop it at runtime — nothing that reads code can. That job belongs to AxiomGate Kernel, which enforces attenuation when the call is actually made, and writes a hash-chained audit record of the decision. It is readable and runnable, but it is source-available under PolyForm Noncommercial, not MIT like this one. Worth saying plainly rather than leaving you to find it: the two are by the same author and they are not licensed the same way.

Built by Robin Svensson.

License

MIT, see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

axiomgate_lint-0.2.2.tar.gz (38.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

axiomgate_lint-0.2.2-py3-none-any.whl (17.9 kB view details)

Uploaded Python 3

File details

Details for the file axiomgate_lint-0.2.2.tar.gz.

File metadata

  • Download URL: axiomgate_lint-0.2.2.tar.gz
  • Upload date:
  • Size: 38.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for axiomgate_lint-0.2.2.tar.gz
Algorithm Hash digest
SHA256 dde66fad06f2b1635359c5295f9f2f58b2201d26785c3732d52f1ffa35e5c44b
MD5 a2e298fbfc69209fedce7f9111ba26bb
BLAKE2b-256 4812fb2a175f6cc6860392c731c6ca84c8fda00a00caf6e2ff24cfd62920a6aa

See more details on using hashes here.

Provenance

The following attestation bundles were made for axiomgate_lint-0.2.2.tar.gz:

Publisher: release.yml on robin-svensson/axiomgate-lint

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file axiomgate_lint-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: axiomgate_lint-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 17.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for axiomgate_lint-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d57309c17185d72581d7b2b95df863b1c23f63054fb82a0cff318c0516354985
MD5 dbe0123ebaad9a17412fbcb87d64a64c
BLAKE2b-256 00e70203f1648856339c85f87b0e23db659f7ca640df4cbcf60b4a9a18b9dc98

See more details on using hashes here.

Provenance

The following attestation bundles were made for axiomgate_lint-0.2.2-py3-none-any.whl:

Publisher: release.yml on robin-svensson/axiomgate-lint

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.2 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page