ln-647-env-config-auditor について
技術スタックに応じて、コード、環境ファイル、コンテナ、CI設定の環境変数を監査するワーカーです。同期、命名、起動時検証の問題と準拠スコアを報告し、ファイルは変更しません。
主な機能
- 01環境設定ファイルの一覧化
- 02コードとサンプル間の変数比較
- 03命名、重複、コメントの確認
- 04起動時検証と機密性の高い既定値の確認
- 05準拠スコアの算出とレポート作成
活用シーン
- 01リリース前の環境変数の整合性確認
- 02プロジェクト内の不足または未使用変数の特定
- 03必須設定の起動時検証の評価
MCP 対応
利用可能な場合、hex-graphでシンボルや参照を分析し、hex-lineでローカル内容を読み取ります。MCPが使えない場合は組み込みツールに切り替えます。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ levnikolaevich/ claude-code-skills --skill ln-647-env-config-auditorインストール後、Claude Code で/ln-647-env-config-auditorを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/audit_output_schema.mdaudit_scoring.mdaudit_summary_contract.mdaudit_worker_core_contract.mdcleanup_evidence_contract.mdconfig_rules.mdevaluation_coordinator_runtime_contract.mdevaluation_parallelism_policy.mdevaluation_research_contract.mdevaluation_summary_contract.mdevaluation_worker_runtime_contract.mdloop_health_contract.mdmcp_integration_patterns.mdmcp_tool_preferences.mdrefinement_trace_contract.mdtwo_layer_detection.mdreferences/scripts/evaluation-runtime/cli.mjsreferences/templates/audit_worker_report_template.md詳細
パス: ファイルパス(
shared/、references/)は、skills リポジトリのルートを基準とします。CWD に見つからない場合は、この SKILL.md ディレクトリを見つけ、1 階層上をリポジトリのルートとします。shared/がない場合は、WebFetch を使用してhttps://raw.githubusercontent.com/levnikolaevich/claude-code-skills/master/skills/{path}からファイルを取得します。
Env Config Auditor(L3 Worker)
種類: L3 Worker
環境変数の設定、同期、衛生状態を監査する専門ワーカーです。
目的と範囲
- コード、.env ファイル、docker-compose、CI 設定における環境変数の設定を監査します
- 4 つのチェックカテゴリ:ファイル一覧、変数の同期、命名と品質、起動時の検証
- 合計 11 項目のチェック(C1.1-C1.3、C2.1-C2.3、C3.1-C3.3、C4.1-C4.2)
- コンプライアンススコア(X/10)を算出します
- スタックに応じて検出します(JS、Python、Go、.NET、Java、Ruby、Rust)
対象外:
- ソースコード内のハードコードされたシークレットの検出(セキュリティ監査担当の領域)
- .gitignore/.dockerignore のパターン(プロジェクト構成監査担当の領域)
- 環境ファイルの生成/スキャフォールディング(ブートストラップ担当の領域)
入力
必ず読み込む: references/audit_worker_core_contract.md を読み込みます。
必ず読み込む: references/mcp_tool_preferences.md と references/mcp_integration_patterns.md を読み込みます。
技術スタック、コードベースのルート、output_dir、domain_mode、scan_path を含む contextStore を受け取ります。
シンボルまたは参照の分析によって環境変数の同期に関する検出結果が大幅に改善される場合は、まず hex-graph を使用します。利用可能な場合は、ローカルのコード/設定を読み取る際にまず hex-line を使用します。MCP を利用できない場合、サポートされていない場合、またはインデックス化されていない場合は、組み込みの Read/Grep/Glob/Bash を使用して続行し、レポートにフォールバックしたことを記載します。
ワークフロー
必ず読み込む: 検出方法について references/two_layer_detection.md を読み込みます。
必ず読み込む: 検出パターンについて references/config_rules.md を読み込みます。
フェーズ 1:コンテキストの解析とスタックの検出
1. Parse: codebase_root, output_dir, tech_stack, domain_mode, scan_path
2. Determine primary language from tech_stack -> select env usage patterns from config_rules.md
3. Determine project type:
- web_service: Express/FastAPI/ASP.NET/Spring -> all checks apply
- cli_tool: Click/Typer/Commander/cobra -> C1.3 optional, C4.1 optional
- library: exports only -> C1.1 optional, C4.1 skip
フェーズ 2:ファイル一覧(レイヤー 1 のみ)
# C1.1: .env.example or .env.template exists
example_files = Glob("{scan_root}/.env.example") + Glob("{scan_root}/.env.template")
IF len(example_files) == 0:
findings.append(severity: "MEDIUM", check: "C1.1",
issue: "No .env.example or .env.template",
recommendation: "Create .env.example documenting all required env vars",
effort: "S")
# C1.2: .env files committed (secrets risk)
env_committed = Glob("{scan_root}/**/.env") + Glob("{scan_root}/**/.env.local")
+ Glob("{scan_root}/**/.env.*.local")
# Exclude safe templates
env_committed = filter_out(env_committed, [".env.example", ".env.template", ".env.test"])
FOR EACH file IN env_committed:
findings.append(severity: "CRITICAL", check: "C1.2",
issue: ".env file committed: {file}",
location: file,
recommendation: "Remove from tracking (git rm --cached), add to .gitignore",
effort: "S")
# C1.3: Environment-specific file structure
docker_compose = Glob("{scan_root}/docker-compose*.yml") + Glob("{scan_root}/docker-compose*.yaml")
IF len(docker_compose) > 0:
env_specific = Glob("{scan_root}/.env.development") + Glob("{scan_root}/.env.production")
+ Glob("{scan_root}/.env.staging")
IF len(env_specific) == 0 AND len(example_files) == 0:
findings.append(severity: "LOW", check: "C1.3",
issue: "Docker Compose present but no env-specific files or .env.example",
recommendation: "Create .env.example with documented vars per environment",
effort: "S")
フェーズ 3:変数の同期(レイヤー 1 + レイヤー 2)
# Step 3a: Extract vars from code (Layer 1)
code_vars = {} # {var_name: [{file, line, has_default, default_value}]}
FOR EACH pattern IN config_rules.env_usage_patterns[tech_stack.language]:
matches = Grep(pattern, scan_root, glob: source_extensions)
FOR EACH match IN matches:
var_name = extract_var_name(match)
code_vars[var_name].append({file: match.file, line: match.line,
has_default: check_default(match), default_value: extract_default(match)})
# Pydantic Settings (Layer 2): detect BaseSettings subclasses
IF tech_stack.language == "python":
settings_classes = Grep("class\s+\w+\(.*BaseSettings", scan_root, glob: "*.py")
FOR EACH cls IN settings_classes:
Read class body -> extract fields -> convert to SCREAMING_SNAKE_CASE
Apply env_prefix if configured
Add to code_vars
# Step 3b: Extract vars from .env.example
example_vars = {} # {var_name: value_or_empty}
IF exists(.env.example):
FOR EACH line IN Read(.env.example):
IF line matches r'^([A-Z_][A-Z0-9_]*)=(.*)':
example_vars[$1] = $2
# Step 3c: Extract vars from docker-compose environment
docker_vars = {} # {var_name: value}
FOR EACH compose_file IN docker_compose:
Parse environment: section(s) -> extract var=value pairs
docker_vars.update(parsed)
# C2.1: Code->Example sync (Layer 2 mandatory)
missing_from_example = code_vars.keys() - example_vars.keys()
FOR EACH var IN missing_from_example:
# Layer 2: filter false positives
IF var IN config_rules.framework_managed[tech_stack.framework]:
SKIP # Auto-managed by framework
ELIF var appears only in test files:
SKIP # Test-only variable
ELSE:
findings.append(severity: "MEDIUM", check: "C2.1",
issue: "Env var '{var}' used in code but missing from .env.example",
location: code_vars[var][0].file + ":" + code_vars[var][0].line,
recommendation: "Add {var} to .env.example with documented default",
effort: "S")
# C2.2: Example->Code sync (dead vars, Layer 2 mandatory)
dead_vars = example_vars.keys() - code_vars.keys()
FOR EACH var IN dead_vars:
# Layer 2: check infrastructure usage
IF Grep(var, docker_compose + Dockerfiles + CI_configs):
SKIP # Used in infrastructure, not dead
ELIF var IN config_rules.framework_managed:
SKIP # Framework auto-reads it
ELSE:
findings.append(severity: "MEDIUM", check: "C2.2",
issue: "Dead var in .env.example: '{var}' (unused in code and infrastructure)",
location: ".env.example",
recommendation: "Remove from .env.example or add usage in code",
effort: "S")
# C2.3: Default desync (Layer 2 mandatory)
# Limit to top 50 vars for token budget
sync_candidates = code_vars where has_default == true AND
(var IN example_vars OR var IN docker_vars)
FOR EACH var IN sync_candidates[:50]:
defaults = {}
IF var has default in code: defaults["code"] = code_default
IF var in example_vars with non-empty value: defaults["example"] = example_value
IF var in docker_vars: defaults["docker-compose"] = docker_value
IF len(defaults) >= 2 AND NOT all_equal(defaults.values()):
findings.append(severity: "HIGH", check: "C2.3",
issue: "Default desync for '{var}': " + format_defaults(defaults),
location: code_vars[var][0].file + ":" + code_vars[var][0].line,
recommendation: "Unify defaults across code, .env.example, docker-compose",
effort: "M")
フェーズ 4:命名と品質(レイヤー 1 + レイヤー 2)
all_vars = set(code_vars.keys()) | set(example_vars.keys())
# C3.1: Naming convention
non_screaming = [v for v in all_vars if NOT matches(r'^[A-Z][A-Z0-9_]*$', v)]
IF len(non_screaming) > 0:
findings.append(severity: "LOW", check: "C3.1",
issue: "Non-SCREAMING_SNAKE_CASE vars: " + join(non_screaming[:10]),
recommendation: "Rename to SCREAMING_SNAKE_CASE for consistency",
effort: "S")
# Prefix consistency check (Layer 2)
prefixes = group_by_concept(all_vars)
# e.g., {database: [DB_HOST, DB_PORT, DATABASE_URL]} -> conflicting prefixes
conflicting = find_conflicting_prefixes(prefixes)
IF conflicting:
FOR EACH group IN conflicting:
findings.append(severity: "LOW", check: "C3.1",
issue: "Inconsistent prefixes: " + join(group.vars),
recommendation: "Standardize to one prefix (e.g., DB_ or DATABASE_)",
effort: "S")
# C3.2: Redundant variables (Layer 2 mandatory)
# Conservative: only flag vars with identical suffixes + overlapping prefixes
FOR EACH suffix IN common_suffixes(all_vars): # e.g., _CACHE_TTL, _TIMEOUT
vars_with_suffix = filter(all_vars, suffix)
IF len(vars_with_suffix) >= 2:
# Layer 2: read usage context
IF vars serve same purpose with same or similar values:
findings.append(severity: "LOW", check: "C3.2",
issue: "Potentially redundant vars: " + join(vars_with_suffix),
recommendation: "Consider unifying into single var",
effort: "M")
# C3.3: Missing comments in .env.example
IF exists(.env.example):
lines = Read(.env.example)
uncommented_vars = 0
FOR EACH var_line IN lines:
IF is_var_declaration(var_line):
preceding = get_preceding_line(var_line)
IF NOT is_comment(preceding) AND NOT is_self_explanatory(var_name):
uncommented_vars += 1
IF uncommented_vars > 3:
findings.append(severity: "LOW", check: "C3.3",
issue: "{uncommented_vars} vars in .env.example lack explanatory comments",
location: ".env.example",
recommendation: "Add comments for non-obvious vars; include generation instructions for secrets",
effort: "S")
フェーズ 5:起動時の検証(レイヤー 1 + レイヤー 2)
# C4.1: Required vars validated at boot
# Layer 1: detect validation frameworks
validation_found = false
FOR EACH framework IN config_rules.validation_frameworks[tech_stack.language]:
IF Grep(framework.detection_pattern, scan_root, glob: source_extensions):
validation_found = true
BREAK
IF NOT validation_found:
# Layer 2: check for manual validation patterns
manual_patterns = [
r'process\.env\.\w+.*throw|if\s*\(!process\.env', # JS
r'os\.getenv.*raise|if not os\.getenv|if\s+os\.environ', # Python
r'os\.Getenv.*panic|os\.Getenv.*log\.Fatal', # Go
]
FOR EACH pattern IN manual_patterns:
IF Grep(pattern, scan_root):
validation_found = true
BREAK
IF NOT validation_found AND project_type == "web_service":
findings.append(severity: "MEDIUM", check: "C4.1",
issue: "No startup validation for required env vars",
recommendation: "Add validation at boot (pydantic-settings, envalid, zod, or manual checks)",
effort: "M")
# C4.2: Sensitive defaults (Layer 1 + Layer 2)
sensitive_patterns = config_rules.sensitive_var_patterns
FOR EACH var IN code_vars WHERE var.has_default == true:
var_upper = var.name.upper()
IF any(pattern IN var_upper for pattern IN sensitive_patterns):
# Layer 2: verify this is truly sensitive
context = Read(var.file, var.line +- 10 lines)
IF default_value is not empty string AND not "changeme" AND not placeholder:
findings.append(severity: "HIGH", check: "C4.2",
issue: "Sensitive default for '{var.name}': hardcoded fallback bypasses env config",
location: var.file + ":" + var.line,
recommendation: "Remove default; require explicit env var or fail at startup",
effort: "S")
フェーズ 6:スコア算出、レポート作成、結果の返却
1. Calculate score:
penalty = (CRITICAL * 2.0) + (HIGH * 1.0) + (MEDIUM * 0.5) + (LOW * 0.2)
score = max(0, 10 - penalty)
2. Build report per references/templates/audit_worker_report_template.md
Include AUDIT-META, Checks table, Findings table
Include DATA-EXTENDED with:
{
tech_stack_detected: string,
env_files_inventory: [{file, type, committed}],
code_vars_count: number,
example_vars_count: number,
sync_stats: {missing_from_example, dead_in_example, default_desync},
validation_framework: string | null
}
3. Write to {output_dir}/ln-647--{identifier}.md (atomic single Write call)
IF domain_mode == "domain-aware": 647-env-config-{domain}.md
4. Return summary per `references/audit_summary_contract.md`.
When `summaryArtifactPath` is absent, write the standalone runtime summary under `.hex-skills/runtime-artifacts/runs/{run_id}/evaluation-worker/{worker}--{identifier}.json` and optionally echo the same summary in structured output.
Report written: {output_dir}/ln-647--{identifier}.md
Score: X.X/10 | Issues: N (C:N H:N M:N L:N)
スコアリングアルゴリズム
必ず読み込む: references/audit_worker_core_contract.md と references/audit_scoring.md を読み込みます。
出力形式
必ず読み込む: references/audit_worker_core_contract.md と references/templates/audit_worker_report_template.md を読み込みます。
references/audit_summary_contract.md に従って JSON サマリーを書き出します。管理モードでは呼び出し元から runId と summaryArtifactPath の両方が渡されます。スタンドアロンモードでは、共有契約に従ってワーカーが実行単位のアーティファクトパスを生成します。
{output_dir}/ln-647--{identifier}.md に、category: "Env Configuration" と以下のチェック項目を含むレポートを書き出します:env_example_exists、env_committed、env_specific_files、code_to_example_sync、example_to_code_sync、default_desync、naming_convention、redundant_vars、missing_comments、startup_validation、sensitive_defaults。
参照ファイル
- 検出パターン:
references/config_rules.md - 監査出力スキーマ:
references/audit_output_schema.md
重要なルール
必ず読み込む: references/audit_worker_core_contract.md を読み込みます。
- 自動修正禁止: 報告のみを行い、.env ファイルやコードは変更しません
- スタックに応じた検出: tech_stack の言語/フレームワークに基づいて検出パターンを選択します
- C2.x と C4.x ではレイヤー 2 が必須: コンテキストによる検証なしにレイヤー 1 の一致を有効とみなしてはいけません
- C2.3 のトークン予算: デフォルト不一致の分析対象は上位 50 個の変数に制限します
- ドメインを考慮したスコープ設定: .env ファイルはプロジェクトルート全体をスキャンし、コード内の変数は
scan_pathの範囲に限定します - 工数の目安: S = 1 時間未満、M = 1~4 時間、L = 4 時間超
- 除外項目: C2.1 ではテストファイルを除外し、config_rules.md に記載されたフレームワーク管理変数をスキップします
完了の定義
- contextStore を解析済み(技術スタック、フレームワーク、output_dir)
- 11 個すべてのチェックを完了済み(C1.1-C1.3、C2.1-C2.3、C3.1-C3.3、C4.1-C4.2)
- 重大度、場所、チェック ID、工数、推奨事項を含む指摘事項を収集済み
-
references/audit_scoring.mdに従ってスコアを算出済み -
{output_dir}/ln-647--{identifier}.mdにレポートを作成済み(単一の Write 呼び出しでアトミックに書き込み) - 契約に従って概要を作成済み
バージョン: 1.0.0 最終更新日: 2026-03-15