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.

Metadata

Release files for axiomgate-lint 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for axiomgate-lint 0.2.2
File Size Uploaded Provenance
axiomgate_lint-0.2.2.tar.gz 38.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for axiomgate-lint 0.2.2
File Interpreter ABI Platform
axiomgate_lint-0.2.2-py3-none-any.whl Python 3 none any Details

Total release size: 55.9 kB

Release files / axiomgate_lint-0.2.2.tar.gz

Download URL axiomgate_lint-0.2.2.tar.gz
Size 38.0 kB
Tags Source
SHA-256 checksum
How to use checksums
dde66fad06f2b1635359c5295f9f2f58b2201d26785c3732d52f1ffa35e5c44b
BLAKE2b-256 checksum
How to use checksums
4812fb2a175f6cc6860392c731c6ca84c8fda00a00caf6e2ff24cfd62920a6aa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / axiomgate_lint-0.2.2-py3-none-any.whl

Download URL axiomgate_lint-0.2.2-py3-none-any.whl
Size 17.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d57309c17185d72581d7b2b95df863b1c23f63054fb82a0cff318c0516354985
BLAKE2b-256 checksum
How to use checksums
00e70203f1648856339c85f87b0e23db659f7ca640df4cbcf60b4a9a18b9dc98
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Security for axiomgate-lint 0.2.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for axiomgate_lint-0.2.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.2 This release

2 release files Full provenance

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