
파이썬 저장소에서 하드코딩된 비밀키를 감지하고 자동 수정합니다 — 수정이 코드를 손상시킬 수 있는 경우에는 거부합니다.
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이 아닌 종료 코드를 초래합니다. 거부된 발견 사항이 있는 파일은 수정되지 않습니다.
파일을 다시 실행해도 변경 사항이 없습니다. 재작성은 원래 들여쓰기와 주석을 유지합니다. 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
| 패턴 | 예제 | 동작 | 이유 |
|---|
| 단순 할당 | 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 누락 | 모든 패턴 | 거부됨 | 변수 이름을 도출할 환경 변수 계약 없음 |