mcpField
L
ln-647-env-config-auditor

ln-647-env-config-auditor

github.com/levnikolaevich测试与质量584116 次安装2 天前更新
代码仓库

关于 ln-647-env-config-auditor

可适配技术栈的环境变量审计 Worker,检查代码、环境文件、容器和 CI 配置。它报告同步、命名及启动验证问题并计算合规分数,但不会修改文件。

主要功能

  1. 01盘点环境配置文件
  2. 02比较代码与示例中的变量
  3. 03检查命名、冗余项和注释
  4. 04检查启动验证及敏感默认值
  5. 05计算合规分数并生成报告

适用场景

  1. 01发布前检查环境变量一致性
  2. 02查找项目文件中的缺失或未使用变量
  3. 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.md
audit_scoring.md
audit_summary_contract.md
audit_worker_core_contract.md
cleanup_evidence_contract.md
config_rules.md
evaluation_coordinator_runtime_contract.md
evaluation_parallelism_policy.md
evaluation_research_contract.md
evaluation_summary_contract.md
evaluation_worker_runtime_contract.md
loop_health_contract.md
mcp_integration_patterns.md
mcp_tool_preferences.md
refinement_trace_contract.md
two_layer_detection.md
references/scripts/evaluation-runtime/
cli.mjs
references/templates/
audit_worker_report_template.md

详细介绍

**路径:**文件路径(shared/、references/)相对于 skills 仓库根目录。如果在当前工作目录中未找到,请定位此 SKILL.md 所在目录,并向上一级查找仓库根目录。如果缺少 shared/,请通过 WebFetch 从 https://raw.githubusercontent.com/levnikolaevich/claude-code-skills/master/skills/{path} 获取文件。

环境配置审计器(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;在独立模式下,Worker 会根据共享契约生成自己的运行范围内工件路径。

将报告写入 {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 token 预算:**默认失配分析最多检查排名前 50 的变量
  • **按领域确定范围:**全局扫描 .env 文件(项目根目录);代码变量限定在 scan_path 范围内
  • **工作量评估:**S = <1h,M = 1-4h,L = >4h
  • **排除项:**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

评论

支持 Markdown