
파이썬 저장소에서 하드코딩된 비밀키를 감지하고 자동 수정합니다 — 수정이 코드를 손상시킬 수 있는 경우에는 거부합니다.
Autonoma는 하드코딩된 시크릿을 탐지하고 AST 재작성을 통해 os.environ[...] 참조로 대체합니다. 변경은 재작성이 결정적이고 의미론적으로 보존되는 경우에만 적용됩니다.
재작성은 구문 분석 트리를 사용합니다 — 원시 텍스트에 대한 정규식이 아닙니다.
네트워크 호출 없이 로컬에서 실행됩니다. 멱등성과 최소한의 diff로 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에서 0이 아닌 종료 코드를 초래합니다. 거부된 발견 사항이 있는 파일은 수정되지 않습니다.
| 패턴 | 예제 | 동작 | 이유 |
|---|---|---|---|
| 단순 할당 | 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 누락 | 모든 패턴 | 거부됨 | 변수 이름을 도출할 환경 변수 계약 없음 |
파일을 다시 실행해도 변경 사항이 없습니다. 재작성은 원래 들여쓰기와 주석을 유지합니다. 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에서 0이 아닌 종료 코드를 초래합니다. 파일에서 하나의 이슈라도 거부되면 해당 파일은 절대 수정되지 않습니다.
<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 License