repomix-safe-mixer
Safely package codebases with repomix by automatically detecting and removing hardcoded credentials before packing. Use when packaging code for distribution, creating reference packages, or when the user mentions security concerns about sharing code with repomix.
1How It Works & Mechanism
Orchestration follows a 'subprocess + JSON contract' pattern: safe_pack.py does not import the scanner directly. Instead, it invokes scan_secrets.py in the same directory via subprocess with --json, parsing its stdout to decide whether to proceed with packaging.
cmd = [sys.executable, str(scan_script), str(directory), '--json']
Detection capabilities stem from a 13-item regex dictionary (SECRET_PATTERNS). All checks are single-line regex matches without AST parsing, entropy analysis, or machine learning.
'aws_access_key': r'(?i)AKIA[0-9A-Z]{16}', 'aws_secret_key': r'(?i)(?:aws_secret|aws.{0,20}secret).{0,20}[=:]\s*["\']?([0-9a-zA-Z/+=]{40})["\']?', 'supabase_url': r'https://[a-z]{20}\.supabase\.co', 'supabase_anon_key': r'eyJ[A-Za-z0-9_-]*\.eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*', 'stripe_key': r'(?:sk|pk)_(live|test)_[0-9a-zA-Z]{24,}',
The scan scope is defined by a dual constraint: an 'extension allowlist + .env filename exception'. Code comments explicitly clarify why an exception is required for dotenv files (Path('.env').suffix == '').
# Full filenames that must be scanned regardless of Path.suffix. Dotenv # files break suffix matching: Path('.env').suffix == '' and # Path('.env.local').suffix == '.local', so scanning by extension alone # silently skips the most common places secrets end up. SCANNABLE_FILENAMES = { '.env',
Skipped directories are defined as a hardcoded set (15 directories including node_modules, .git, .venv, dist, build, etc.) without parsing .gitignore.
SKIP_DIRS = { 'node_modules', '.git', '.venv', 'venv', '__pycache__', 'dist', 'build',
False positive suppression relies on two strict rules: hits matching placeholder wordlists are skipped, and a substring check is evaluated against the entire line (if words like 'example' or 'test-' appear anywhere in the line, all hits on that line are discarded).
placeholders = [ 'your-', 'example', 'placeholder', 'xxx', 'yyy', 'zzz', 'test-', 'demo-', 'sample-', '<YOUR_', '${', 'TODO' ]
Comment lines are skipped unconditionally (starting with //, #, /*, or *).
if re.search(r'^\s*(?://|#|/\*|\*)', line):
Defaults to fail-closed: detection immediately aborts packaging with exit 1, and execution proceeds only when explicitly passed --force; the --force path still emits a warning.
print("❌ Cannot pack: Secrets detected!")
Packaging is executed by calling the external repomix CLI: the target directory is set as cwd, --config/--output arguments are passed through, and repomix writes the artifacts to disk.
result = subprocess.run(cmd, cwd=directory, capture_output=True, text=True)
The report echoes matched secret snippets and context lines to stdout (in --json mode, match is truncated to 50 characters and context to 100 characters; human-readable reports print the first 80 characters of match/context), but the script itself writes no files.
print(f" Match: {finding.matched_text[:80]}") print(f" Context: {finding.line_content[:80]}")
Exit codes serve as the interface: returns 1 when matches are found and 0 when clean. SKILL.md explicitly suggests using it as a pre-commit hook (to block commits).
sys.exit(1 if findings else 0)
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| cli | repomix(第三方打包 CLI,需用户自行预装) |
| cli | python3(sys.executable 拉起同目录扫描器;仅标准库 os/re/sys/json/subprocess/pathlib) |
| package | repomix(npm 生态包,本 skill 只调用命令,不负责安装) |
4Recommended Governance & Evidence
- scripts/: Executing local scripts or commands: python3 scripts/safe_pack.py <directory> [--output file.xml] [--config repo
- 环境变量 / API Key: Reading vendor service credentials: Designed to read file contents within the target directory for regex matching, including .env/.env.example/.env.local/.env.production/.env.de
- 本地工作目录: File I/O and artifact generation: Zero file writes from both scripts themselves (no open(...,'w'), no temp files, no log files)
5Second-Pass Verification
- [ok] 外部依赖调用点反查(repomix / python3) — 全目录 grep subprocess 仅 3 处命中:safe_pack.py 的 import、L24 subprocess.run(调 scan_secrets.py,cmd 来自 cmd = [sys.executable, str(scan_script), …])、L74 subprocess.run(cmd, cwd=directory)(cmd 起点为 cmd = ['repomix'])。两个依赖条目均有真实调用点,无第三处。
- [ok] 网络外发反例查找 — grep requests|urllib|urlopen|socket|http|curl|wget 在两个脚本内零命中;SKILL.md 与 common_secrets.md 中的 https:// 全部是占位符(https://your-project-ref.supabase.co)、示例域名(.r2.cloudflarestorage.com 出现在正则 lookahead 文本中)或文档外链。无 DNS/HTTP 客户端、无遥测、无任何外发路径。
- [discrepancy] 扫描器返回码未被校验 → fail-open 反例 — run_secret_scan 只做 result = subprocess.run(...);随后 findings = json.loads(result.stdout) if result.stdout.strip() else [],从不检查 result.returncode。若扫描器因解释器缺失/脚本损坏/进程被杀而 stdout 为空,findings 被判为空列表,main 直接进入 '📦 Packing … with repomix' 分支——即『扫描失败』被当成『无密钥』。这与 SKILL.md『3. Blocks packaging if secrets found』的强承诺存在实现缺口:只承诺了『发现密钥时阻断』,未覆盖『扫描本身失败』。
- [discrepancy] SKILL.md 检测类型清单 vs SECRET_PATTERNS 实际键 — SKILL.md 列出 11 类(AWS Access Keys、R2 Account IDs/Access Keys、Supabase URL/Anon Keys、Stripe、OpenAI sk-、Google Gemini AIza、Generic API Keys、JWT、OAuth Client Secrets、Private Keys、Turnstile)。代码 SECRET_PATTERNS 实为 12 个键,但其中:无 R2 Access Keys 专用规则;无 Google Gemini(AIza…)规则(全目录 grep 'AIza' 零命中);OpenAI sk- 仅在同时出现 api_key/apikey 字样时可被 generic_api_key 捕获,stripe_key 只认 sk_live/sk_test 形式,因此裸的 sk-proj-… 赋值不一定会命中。文档检测类型清单宽于代码能力。
- [discrepancy] 假阳性抑制范围 vs 文档描述 — SKILL.md『Common False Positives』写的是跳过占位符、『Files matching .*test.*, .*example.*, .*sample.*』的文件与注释行。should_skip_match 实际对整行做 substring 包含判断(placeholder in line_lower),且注释判定用 ^\s*(?://|#|/\*|\*)——即抑制粒度是『行』而非『match』或『文件』:真密钥与 'example' 同行即被放过,被注释掉的真实凭证亦不上报。代码抑制面大于文档描述,属漏报向的偏差。
- [discrepancy] 扫描面缺口:不在白名单的高风险文件类型 — SCANNABLE_EXTENSIONS 只含 .ts/.tsx/.js/.jsx/.py/.md/.json/.yaml/.yml/.sh/.bash/.zsh/.sql/.go/.java/.rb/.php/.cs,SCANNABLE_FILENAMES 只补 7 个 dotenv 名。因此 *.pem、*.key、id_rsa、*.ppk、*.p12、*.crt、.envrc、*.toml/*.ini/*.conf/*.properties/*.xml 都不被扫描——private_key 规则(-----BEGIN … PRIVATE KEY-----)写好了却基本没有机会命中这些最常见的私钥载体。SKILL.md 未提示该覆盖边界(仅笼统说 may not catch all credential types)。
- [discrepancy] --force 语义与文档措辞一致性 — SKILL.md 标题写『**Force pack (dangerous, skip scan)**』,暗示跳过扫描;safe_pack.py 中 force = '--force' in sys.argv 之后仍照常执行 run_secret_scan 与 print_findings_report,只是跳过阻断并打印『WARNING: --force flag set, packing anyway despite secrets found!』。实际语义是『扫描照做、仅放行』,措辞『skip scan』不准确。
- [ok] 证据是否指向被扫文件内容/是否落盘 — 两脚本无 open(...,'w')、无 tempfile、无日志写入;匹配结果只经 print/ json.dumps 到 stdout(scan_secrets 的 to_dict 将 match 截断 50、context 截断 100;人类可读报告打印前 80 字符)。『不落盘、不外发』成立;但『不进日志』不成立——终端输出在 agent 会话中会被留存为上下文。
6Verdict & Scenarios
7847e6113e529f6b…