
Обнаруживает и автоматически исправляет жестко закодированные секреты в Python-репозиториях — отказывается, когда исправление может сломать ваш код.
Autonoma обнаруживает жёстко зашитые секреты и заменяет их ссылками на os.environ[...] с помощью перезаписи AST. Изменения применяются только тогда, когда перезапись является детерминированной и сохраняет семантику.
Перезаписи используют разобранное синтаксическое дерево, а не регулярные выражения по сырому тексту.
Работает локально, без сетевых вызовов. Работает в CI с идемпотентными, минимальными изменениями.

Жёстко зашитые секреты в кодовых базах:
Большинство инструментов их обнаруживают.
Autonoma трогает только то, что может гарантированно безопасно изменить.
autonoma scan .
autonoma fix .
git diff
pip install autonoma-cli
Добавьте это в ваш .pre-commit-config.yaml, чтобы предотвратить попадание секретов в историю:
- repo: local
hooks:
- id: autonoma
name: Autonoma Scan
entry: autonoma scan
language: system
types: [python]
# 1. Создайте тестовый файл с жёстко зашитыми секретами
cat > test_secrets.py << 'EOF'
SENDGRID_API_KEY = "sg-live-abc123xyz789"
DB_PASSWORD = "Pr0dAccess2024!"
EOF
# Также создайте файл контракта окружения (требуется для безопасного исправления)
printf 'SENDGRID_API_KEY=\nDB_PASSWORD=\n' > .env.example
# 2. Сканирование — выводит результаты в JSON на stdout
autonoma scan test_secrets.py
# 3. Исправление — перезаписывает файл на месте
autonoma fix test_secrets.py
# 4. Сканирование снова — теперь должно быть чисто (код выхода 0)
autonoma scan test_secrets.py
# 5. Просмотрите результат
cat test_secrets.py
Ожидаемый вывод после исправления:
import os
SENDGRID_API_KEY = os.environ["SENDGRID_API_KEY"]
DB_PASSWORD = os.environ["DB_PASSWORD"]
Режим обнаружения. Выводит JSON в stdout и человекочитаемую сводку в stderr. Без изменений — никогда не модифицирует файлы.
# Сканирование директории (результаты JSON в stdout)
autonoma scan src/
# Сохранение JSON-результатов в файл
autonoma scan src/ > findings.json
Коды выхода для scan:
| Код | Значение |
|---|---|
0 | Находок нет |
1 | Есть находки |
3 | Ошибка инструмента |
Исправляет жёстко зашитые секреты с помощью перезаписи AST. Изменяет файлы на месте.
# Применить исправления
autonoma fix src/
# Предварительный просмотр изменений до записи
autonoma fix src/ --diff
# Записать журнал аудита исправлений
autonoma fix src/ --report-out audit.json
Коды выхода для fix:
| Код | Значение |
|---|---|
0 | Находок нет — репозиторий уже был чистым |
1 | Находки существовали до исправления (исправление могло быть успешным — проверьте количество FIXED/REFUSED в выводе) |
3 | Ошибка инструмента |
Команда
fixзавершается с кодом1всегда, когда перед попыткой исправления были найдены секреты, независимо от того, удалась перезапись или нет. Это сделано намеренно: конвейеры CI должны помечать коммит, в котором были внесены секреты, даже после автоправки. После этого запуститеautonoma scan, чтобы убедиться, что репозиторий чист.
Сканирует историю git на предмет секретов, которые были закоммичены, а затем удалены или изменены.
[!NOTE] Только обнаружение. Эта команда не перезаписывает историю git и не изменяет коммиты.
autonoma history-scan .
Вот шаблоны, которые Autonoma на самом деле исправляет сегодня.
# settings.py
SENDGRID_API_KEY = "sg-live-abc123xyz789"
DB_PASSWORD = "Pr0dAccess2024!"
autonoma fix .)# settings.py
import os
SENDGRID_API_KEY = os.environ["SENDGRID_API_KEY"]
DB_PASSWORD = os.environ["DB_PASSWORD"]
# f-строка — отказано, потому что перезапись изменит семантику
api_key = f"prefix_{BASE_KEY}"
# → REFUSED: refuse_fstring_mixed_expression
# Словарь/вложенное значение — отказано, потому что цель не является простым присваиванием
DATABASES = {
"default": {"PASSWORD": "Pr0d@ccess2024!"}
}
# → REFUSED: unsupported assignment target type
Отклонённые находки сообщаются в JSON-выводе и вызывают ненулевой код выхода в CI. Файлы с отклонёнными находками никогда не изменяются.
Повторный запуск на чистом файле не производит изменений. Перезаписи сохраняют исходные отступы и комментарии. import os добавляется только в том случае, если его нет.
Чтобы завершать сборку с ошибкой при обнаружении секретов:
- name: Scan for secrets
run: autonoma scan .
Команда analyze сохранена для обратной совместимости. Перейдите на scan или fix.
# Эквивалент 'autonoma scan'
autonoma analyze src/ --detect-only
# Эквивалент 'autonoma fix'
autonoma analyze src/ --auto-fix
API_KEY = "secret"class Config: PASS = "secret"connect(password="secret")A = B = "secret") или распаковка кортежей..env.example или контракт окружения.Отклонённые случаи сообщаются в JSON-выводе и вызывают ненулевые коды выхода в CI. Файл никогда не изменяется, если хотя бы одна проблема в нём отклонена.
<file>.bak перед изменением.SEC002 в настоящее время объединяет шаблоны значений учётных данных с логикой маршрутизации, обусловленной ключевыми словами. Учётные данные, присвоенные нетипичным именам переменных, могут в настоящее время не обнаруживаться.
Autonoma намеренно избегает широкого обнаружения секретов только на основе энтропии, если влияние точности не может быть измерено на размеченном корпусе ложноположительных срабатываний.
autonoma scan выводит отчёт detect-only на 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..."
}
]
}
Лицензия MIT
| Шаблон | Пример | Поведение | Почему |
|---|
| Простое присваивание | api_key = "sk-abc123" | Исправлено | Детерминированная перезапись AST |
| Атрибут класса | class C: SECRET = "abc" | Исправлено | Детерминированная перезапись AST |
| Именованный аргумент | connect(password="abc") | Исправлено | Детерминированная перезапись AST |
| f-строка | key = f"prefix_{v}" | Отказано | Перезапись изменит поведение во время выполнения |
| Конкатенация | key = "sk-" + suffix | Отказано | Перезапись изменит поведение во время выполнения |
| Словарь/вложенное значение | cfg = {"pass": "abc"} | Отказано | Не является простой целью присваивания |
| Множественное присваивание | A = B = "secret" | Отказано | Неоднозначная цель |
| Уже безопасно | key = os.getenv("KEY") | Пропущено | Изменения не требуются |
Отсутствует .env.example | любой шаблон | Отказано | Нет контракта окружения для получения имени переменной |