
Autonoma は、ハードコードされたシークレットを検出し、AST 書き換えを使用して os.environ[...] 参照に置き換えます。 変更は、書き換えが決定論的で意味を保持している場合にのみ適用されます。
書き換えは、解析された構文木(生テキストに対する正規表現ではない)を使用します。
ローカルで実行され、ネットワーク呼び出しはありません。冪等で最小限の差分で 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
# また、env 契約ファイルを作成(安全な修正に必要)
printf 'SENDGRID_API_KEY=\nDB_PASSWORD=\n' > .env.example
# 2. スキャン — JSON 結果を標準出力に出力
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 結果を標準出力に)
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-string — 書き換えが意味を変えるため拒否
api_key = f"prefix_{BASE_KEY}"
# → REFUSED: refuse_fstring_mixed_expression
# Dict/ネストされた値 — 対象が単純な代入ではないため拒否
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 レポートを標準出力に出力します:
{
"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-string | key = f"prefix_{v}" | 拒否 | 書き換えが実行時動作を変更する可能性 |
| 連結 | key = "sk-" + suffix | 拒否 | 書き換えが実行時動作を変更する可能性 |
| Dict/ネストされた値 | cfg = {"pass": "abc"} | 拒否 | 単純な代入対象ではない |
| 多重代入 | A = B = "secret" | 拒否 | 対象が曖昧 |
| 既に安全 | key = os.getenv("KEY") | スキップ | 変更不要 |
.env.example がない | 任意のパターン | 拒否 | 変数名を導出するための env 契約がない |