Skip to main content

AST-based detection and safe remediation of hardcoded secrets in Python and config files

Project description

Autonoma

Python License Platform Edition PyPI Version

Autonoma detects hardcoded secrets and replaces them with os.environ[...] references using AST rewrites. Changes are applied only when the rewrite is deterministic and semantically-preserving.

Rewrites use the parsed syntax tree — not regex on raw text. Runs locally with no network calls. Works in CI with idempotent, minimal diffs.

Autonoma Demo


What problem this solves

Hardcoded secrets in codebases:

  • secrets get committed and stay in git history
  • fixing them manually breaks code or misses edge cases
  • teams detect leaks but avoid auto-fix tools because they are unsafe

Most tools detect them.
Autonoma only touches what it can prove is safe to change.


Quick example

autonoma scan .
autonoma fix .
git diff

Installation

pip install autonoma-cli

Pre-commit Integration

Add this to your .pre-commit-config.yaml to prevent secrets from entering your history:

- repo: local
  hooks:
    - id: autonoma
      name: Autonoma Scan
      entry: autonoma scan
      language: system
      types: [python]

Try it in 60 seconds

# 1. Create a test file with hardcoded secrets
cat > test_secrets.py << 'EOF'
SENDGRID_API_KEY = "sg-live-abc123xyz789"
DB_PASSWORD = "Pr0dAccess2024!"
EOF

# Also create the env contract file (required for safe remediation)
printf 'SENDGRID_API_KEY=\nDB_PASSWORD=\n' > .env.example

# 2. Scan — emits JSON findings to stdout
autonoma scan test_secrets.py

# 3. Fix — rewrites the file in place
autonoma fix test_secrets.py

# 4. Scan again — should now be clean (exit 0)
autonoma scan test_secrets.py

# 5. Inspect the result
cat test_secrets.py

Expected output after fix:

import os
SENDGRID_API_KEY = os.environ["SENDGRID_API_KEY"]
DB_PASSWORD = os.environ["DB_PASSWORD"]

Commands

scan

Detection mode. Outputs JSON to stdout and a human-readable summary to stderr. Non-mutating — never modifies files.

# Scan a directory (JSON findings to stdout)
autonoma scan src/

# Save JSON results to a file
autonoma scan src/ > findings.json

Exit codes for scan:

Code Meaning
0 No findings
1 Findings detected
3 Tool error

fix

Remediates hardcoded secrets using AST rewrites. Mutates files in place.

# Apply fixes
autonoma fix src/

# Preview patches before writing
autonoma fix src/ --diff

# Write remediation audit log
autonoma fix src/ --report-out audit.json

Exit codes for fix:

Code Meaning
0 No findings — repo was already clean
1 Findings existed before remediation (remediation may have succeeded — check output for FIXED/REFUSED counts)
3 Tool error

The fix command exits 1 whenever it found secrets before attempting remediation, regardless of whether the rewrite succeeded. This is intentional: CI pipelines should flag the commit where secrets were introduced, even after auto-fix. Run autonoma scan afterward to confirm the repo is clean.

history-scan

Scans git history for secrets that were committed and later removed or changed.

[!NOTE] Detection only. This command does not rewrite git history or modify commits.

autonoma history-scan .

Before / After

These are the patterns Autonoma actually fixes today.

Before

# settings.py
SENDGRID_API_KEY = "sg-live-abc123xyz789"
DB_PASSWORD = "Pr0dAccess2024!"

After (autonoma fix .)

# settings.py
import os
SENDGRID_API_KEY = os.environ["SENDGRID_API_KEY"]
DB_PASSWORD = os.environ["DB_PASSWORD"]

Refused (refusal-first safety)

# f-string — refused because the rewrite would change semantics
api_key = f"prefix_{BASE_KEY}"
# → REFUSED: refuse_fstring_mixed_expression

# Dict/nested value — refused because the target is not a simple assignment
DATABASES = {
    "default": {"PASSWORD": "Pr0d@ccess2024!"}
}
# → REFUSED: unsupported assignment target type

Refused findings are reported in the JSON output and cause a non-zero exit in CI. Files with refused findings are never modified.


What Autonoma fixes vs refuses

Pattern Example Behavior Why
Simple assignment api_key = "sk-abc123" Fixed Deterministic AST rewrite
Class attribute class C: SECRET = "abc" Fixed Deterministic AST rewrite
Keyword argument connect(password="abc") Fixed Deterministic AST rewrite
f-string key = f"prefix_{v}" Refused Rewrite would change runtime behavior
Concatenation key = "sk-" + suffix Refused Rewrite would change runtime behavior
Dict/nested value cfg = {"pass": "abc"} Refused Not a simple assignment target
Multiple assignment A = B = "secret" Refused Ambiguous target
Already safe key = os.getenv("KEY") Skipped No change needed
Missing .env.example any pattern Refused No env contract to derive variable name

CI/CD Features

Re-running on a clean file makes no changes. Rewrites keep original indentation and comments. import os is added only if it's missing.

Integration & CI/CD

GitHub Actions (Scan Only)

To fail your build if any secrets are detected:

- name: Scan for secrets
  run: autonoma scan .

Legacy Commands

analyze is retained for backwards compatibility. Migrate to scan or fix.

# Equivalent to 'autonoma scan'
autonoma analyze src/ --detect-only

# Equivalent to 'autonoma fix'
autonoma analyze src/ --auto-fix

Constraints & Behaviors

What it remediates

  • Simple assignments: API_KEY = "secret"
  • Class attributes: class Config: PASS = "secret"
  • Keyword arguments: connect(password="secret")

What it refuses (by design)

  • Complex Expressions: f-strings, concatenations, or function calls on the RHS.
  • Ambiguous Targets: Multiple assignments (A = B = "secret") or tuple unpacking.
  • Nested/Dict Values: Values inside dicts, lists, or tuples.
  • Missing Context: If no .env.example or environment contract is found in the repo.

Refused cases are reported in JSON output and will cause non-zero exit codes in CI. The file is never modified if any issue in it is refused.

What it does not do

  • It does not use entropy/guessing (it uses heuristic name matching).
  • It does not modify non-Python files in the Community Edition.
  • It does not delete your code; backups are written as <file>.bak before modification.

Current limitations

SEC002 currently combines credential value patterns with keyword-conditioned routing logic. Credentials assigned to highly atypical variable names may not currently be detected.

Autonoma intentionally avoids broad entropy-only secret detection unless precision impact can be measured against a labeled false-positive corpus.


JSON Schema

autonoma scan outputs a detect-only report to stdout:

{
  "schema_version": "1.0",
  "tool_name": "autonoma",
  "tool_version": "0.1.5",
  "generated_at": "2026-03-24T12:00:00Z",
  "mode": "detect-only",
  "summary": {
    "files_processed": 3,
    "total_findings": 2,
    "safe_to_fix": 1,
    "refused": 1
  },
  "findings": [
    {
      "file": "settings.py",
      "line": 4,
      "pattern_type": "api_key",
      "severity": "high",
      "rule_id": "SEC002",
      "safe_to_fix": true,
      "suggested_env_var": "SENDGRID_API_KEY",
      "refusal_reason": null,
      "fingerprint": "sha256:abc123..."
    }
  ]
}

License

MIT License

Project details


Download files

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

Source Distribution

autonoma_cli-0.1.9.tar.gz (123.0 kB view details)

Uploaded Source

Built Distribution

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

autonoma_cli-0.1.9-py3-none-any.whl (52.1 kB view details)

Uploaded Python 3

File details

Details for the file autonoma_cli-0.1.9.tar.gz.

File metadata

  • Download URL: autonoma_cli-0.1.9.tar.gz
  • Upload date:
  • Size: 123.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for autonoma_cli-0.1.9.tar.gz
Algorithm Hash digest
SHA256 4a1024f3c38121e2018a4f3ea059adfc9b38e6f38986241136f2b32c48d894a1
MD5 c02895bdcf16f5ce675231205d1dda4c
BLAKE2b-256 0ff3e33ce2d087b8cc1e0cc701342f6b8718cbdac5b2df247cd1afc6292132ce

See more details on using hashes here.

Provenance

The following attestation bundles were made for autonoma_cli-0.1.9.tar.gz:

Publisher: publish.yml on VihaanInnovations/autonoma

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

File details

Details for the file autonoma_cli-0.1.9-py3-none-any.whl.

File metadata

  • Download URL: autonoma_cli-0.1.9-py3-none-any.whl
  • Upload date:
  • Size: 52.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for autonoma_cli-0.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 476d5df83d70b68c22be010279d5becacfa3db6845fec9816917c94eca2a4b99
MD5 629838b70874453d6ce00577c6603475
BLAKE2b-256 37a44c49a6c12171d7f140711b0c48da0843fef24bb719a0fef8e1f247dd9370

See more details on using hashes here.

Provenance

The following attestation bundles were made for autonoma_cli-0.1.9-py3-none-any.whl:

Publisher: publish.yml on VihaanInnovations/autonoma

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page