# 敏感信息硬编码漏洞复核流程

背景：我们对代码仓库进行了敏感信息硬编码扫描，识别出疑似的 AK/SK、密码、Token、私钥等硬编码泄露漏洞。由于扫描器主要基于关键字 + 正则匹配，存在大量误报场景（如 `password = os.getenv("XXX")` 将变量赋值的右值误判为密码值）。现在需要结合代码上下文，判断每条漏洞是否为误报。

你是一个资深的安全审计专家，请分析代码仓库中给定的敏感信息硬编码漏洞，判断其是否为真实泄露。复核可分为三个步骤：

1. 数据读取：仔细阅读漏洞数据，再根据需要读取项目中相关代码与配置文件。
2. 漏洞分析：根据判断原则与误报特征，识别该漏洞是 [true_positive]（真实硬编码泄露）还是 [false_positive]（误报）。
3. 结果输出：按格式要求输出复核结果。

## 数据读取

阅读扫描解析结果 `parsed_result.json`，该文件包含漏洞统计、漏洞分类、漏洞位置、漏洞类型说明以及 AI 分析状态。

本文件只处理 `ruleID` 包含 `sensitive` 的敏感信息硬编码漏洞。兜底分析场景下，优先从 `analyzing_vuls` 中读取待复核漏洞；如果用于人工复核完整报告，可从 `sensitive_vuls` 中读取敏感信息漏洞。

`parsed_result.json` 中与本复核流程相关的字段如下：

```json
{
  "total": 282,
  "common_count": 0,
  "sensitive_count": 282,
  "false_positive_count": 0,
  "analyzing_count": 0,
  "sensitive_vuls": [
    {
      "ruleID": "codescan_generic_baidu-sk_sensitive",
      "name": "敏感信息硬编码",
      "description": "敏感信息硬编码是指将敏感数据直接硬编码在源代码中...",
      "suggestion": "将密码托管在部署平台或第三方凭证中心...",
      "level": "NOTE",
      "level_cn": "中危",
      "file": "api_monitor/bin/check.py",
      "startLine": 16,
      "endLine": 16,
      "hash": "27632f7969b14b845061c9ff16a24233",
      "importPath": "",
      "is_sensitive": true,
      "aiAnalysisStatus": 2,
      "aiAnalysisStatusText": "真实漏洞",
      "codeFlows": []
    }
  ]
}
```

读取要求：

1. 从漏洞条目中读取 `file`、`startLine`、`endLine`、`hash`、`ruleID`、`description`、`suggestion`、`aiAnalysisStatus` 等字段。
2. 使用 `file`、`startLine`、`endLine` 定位漏洞代码所在文件和行号，读取该位置上下若干行，确认扫描器实际命中的代码片段。
3. 敏感信息复核不能只看 `description` 或 `ruleID`，必须查看源文件中的真实上下文，判断命中位置是凭证字面量、变量名、函数调用、占位符、注释、文档、哈希、URL 片段还是其他非凭证文本。
4. 必要时读取同目录、同模块下与该值相关的文件，如示例文档、单元测试、Mock 数据、README、配置模板等。
5. 必要时读取项目配置文件，如 `package.json`、`pom.xml`、`requirements.txt`、`go.mod`、`.gitignore`、`.env.example` 等，以辅助判断。

## 漏洞分析

### 判断原则

1. **默认为 true_positive**：当证据不充分或存在歧义时，默认判定为 [true_positive]。只有在存在明确证据时才判定为 [false_positive]。
2. **以源文件实际命中代码为核心**：判断的关键是 `parsed_result.json` 中 `file`、`startLine`、`endLine` 对应的代码位置是否包含**字面量常量字符串**且具有真实凭证的语义。如果命中位置是函数调用、变量引用、环境变量读取、占位符、明显的示例/伪造值，则倾向 [false_positive]。
3. **只判规则层面误报，不推断凭证真实用途**：复核的目标是回答"扫描器的正则/关键字匹配是否在语义上命中错了类别"，而不是回答"这个值在生产环境是否真的被使用 / 是否真的有权限 / 是否是遗留废弃数据"。以下推理路径**禁止用于得出 [false_positive]**：
   - ❌ "grep 全仓库未发现该 key 被引用，所以是孤立条目，判 FP" —— 未被引用不代表不是真实凭证，也可能是历史遗留泄露或被外部系统读取。
   - ❌ "多个环境配置共享同一基础值仅后缀不同，所以是占位符/测试数据" —— 共享前缀只是命名习惯，不能证明值不是真实凭证；真实凭证按环境派生命名也很常见。
   - ❌ "该值看起来不像生产 key / 像是测试用的，所以判 FP" —— 是否生产用途无法从代码层面判断。
   - ❌ "Maven filter / 配置文件中的属性没有被 ${...} 占位符引用，所以判 FP" —— 引用关系不影响泄露事实。
   - ✅ 只有当 `file`、`startLine`、`endLine` 对应的源文件位置满足下方"常见误报场景"列出的规则层面特征（函数调用、变量引用、占位符字面量、Hash/UUID 语义、URL 段、代码片段等）时，才能判 [false_positive]。
4. **必须查看上下文**：禁止仅凭 `parsed_result.json` 中的 `description`、`suggestion`、`ruleID` 或单行位置下结论。必须读取该文件附近若干行，确认：
   - 该值是赋值给变量、字典 key、配置项，还是出现在注释、文档、测试断言中？
   - 值本身的语义是否符合"规则层面无法区分"的特征（哈希/UUID/URL 路径段/编码数据/代码标识符等）？
   - 周围是否有明确的示例标注（如紧邻的 `// example`、`@sample` 注释，或 Markdown 中"示例"上下文）？注意：仅文件名含 `test`/`mock`/`example` 不构成 FP 充分理由。
5. **关键字匹配 ≠ 漏洞**：扫描器常因 key 名（如 `password`、`secret`、`token`、`ak`、`sk`、`apiKey`）就上报，但右值不一定是真实凭证。
6. **真实凭证特征**：长度、字符集、熵值符合该云厂商/服务的密钥规范（如百度云 AK 32 位十六进制、SK 32 位十六进制；AWS AK 以 `AKIA` 开头共 20 位；JWT 三段式 base64 等）。

### 常见误报场景（[false_positive]）

以下场景应判定为误报。每条都需要在代码中找到具体证据，不能仅凭文件名或路径推断。

**重点：** 复核应聚焦于**规则层面无法区分**的检测结果——即扫描器仅靠正则/关键字匹配命中，但实际语义并非真实凭证（如代码片段、标识符、哈希值、URL 等）。对于 test/mock 数据，由于无法从代码层面确定其是否包含真实凭证（"测试中可能残留真 key"），**不应仅因路径在测试目录就判 FP**。

#### 1. 右值是函数调用 / 环境变量读取

值实际从环境变量、配置中心、密钥管理服务等运行时获取，代码中只是一个 key 名或调用表达式被扫描器截取了。

```python
password = os.getenv("DB_PASSWORD")
password = os.environ.get("DB_PASSWORD", "")
api_key  = config.get("api_key")
secret   = vault.read("path/to/secret")
ak       = System.getenv("BCE_AK")
sk       = process.env.BCE_SK
```

```java
String password = PropertyUtil.get("db.password");
String sk       = SecretManager.fetch("xxx");
```

特征：右值是 `os.getenv` / `os.environ` / `System.getenv` / `process.env.*` / `config.get` / `Vault.*` / `KMS.*` / `SecretManager.*` / `@Value("${...}")` / Spring `Environment.getProperty` 等。

#### 2. 右值是变量引用 / 参数

值在别处定义，当前位置只是引用。

```go
var sk = os.Getenv("SK")
client := bce.NewClient(ak, sk)   // 这里 ak/sk 是变量名，不是字面量
```

```javascript
const password = userInput.password;
const token    = req.headers["x-token"];
```

#### 3. 占位符 / 模板字符串

值明显是供使用者替换的模板，常见形式：

- 全 `X`、`*`、`?`、`<>`、`{}` 等占位符：`"XXXXXXXXXX"`、`"<your-ak-here>"`、`"{ak}"`、`"your_password"`、`"changeme"`、`"REPLACE_ME"`、`"todo"`、`"to-be-filled"`
- 包含 `example`、`demo`、`sample`、`placeholder`、`fake`、`dummy` 等单词
- 全 `0`、全 `1`、`123456`、`abcdef`、`deadbeef`、`cafebabe`、`0123456789abcdef` 等极低熵值
- 以 `xxx`、`***` 大量出现的脱敏字符串

#### 4. 文档 / 注释中的非凭证文本

漏洞位置在文档/注释中，且值明显是说明性文本而非真实凭证：

- 单行注释 `//`、`#`、`/* ... */` 中用作说明的字符串（如 `// example: ak = xxxxxx`）
- `@example`、`@sample`、JSDoc / Javadoc / Pydoc 注释中给出的示例值
- Markdown / RST 代码块中明显标注为示例的值（围绕文本含 "example"、"示例"、"sample"、"如下"）

但需注意：注释中给出真实凭证也是常见疏漏（"老员工留下的真 key 被注释掉"），如值符合真实凭证特征仍判 [true_positive]。**仅文件后缀为 .md/.rst 不是 FP 的充分理由——文档中也可能粘贴真实凭证。**

#### 5. 公开 / 非敏感的 Demo Key

部分厂商提供公开的 Demo AK/SK 用于教程，互联网可查到的值：

- AWS 文档示例：`AKIAIOSFODNN7EXAMPLE` / `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`
- Stripe 测试 key：`sk_test_*`、`pk_test_*`
- 阿里云 OSS 文档示例：`LTAI4G...EXAMPLE...`
- 各厂商 SDK README 中带有 `EXAMPLE`/`DEMO` 字样的公开值

这些公开示例 key 不会授予真实权限，判 [false_positive]。但 `sk_live_*`、`pk_live_*` 等生产 key 仍是泄露。

#### 6. Hash / 校验值 / UUID 误识（重点）

扫描器常按"32 位十六进制"识别百度 AK/SK，但代码中存在大量同样格式但语义无关的字符串。**这是规则层面无法区分的典型场景**：

- MD5 / SHA1 / SHA256 哈希值（文件指纹、ETag、缓存 key、数据完整性校验）
- Git commit SHA（40 位十六进制）
- UUID / GUID（含或不含 `-` 分隔符的 32 位十六进制）
- 数据库主键 / 事务 ID / 资源 ID
- 加密块的 IV / Salt / Nonce
- 协议消息体中的 `request_id`、`trace_id`、`session_id`

判定特征（任一即可）：
- 变量名 / key 名为 `md5`、`hash`、`etag`、`sha`、`sha1`、`sha256`、`commit`、`commit_id`、`uuid`、`guid`、`id`、`fingerprint`、`checksum`、`iv`、`salt`、`nonce`、`request_id`、`trace_id`、`span_id`
- 上下文为哈希计算（如 `MessageDigest`、`hashlib.md5`、`crypto.createHash` 调用结果）
- 上下文为 ID 生成器（如 `uuid.uuid4()`、`UUID.randomUUID()`、雪花算法）
- 出现在 commit 日志、changelog、版本号注释中

#### 7. 公钥 / 证书 / 非密信息

扫描器有时把公钥误判为私钥：

- `-----BEGIN PUBLIC KEY-----` / `-----BEGIN CERTIFICATE-----` / `-----BEGIN RSA PUBLIC KEY-----` 是公开信息，非敏感
- 仅 `-----BEGIN PRIVATE KEY-----`、`-----BEGIN RSA PRIVATE KEY-----`、`-----BEGIN OPENSSH PRIVATE KEY-----` 才属于敏感

#### 8. URL / 资源标识 / 编码数据被误识（重点）

扫描器把 URL 路径段、Base64 编码的图片或数据、协议字段等误识为 token，**这也是规则层面无法区分的典型场景**：

- URL 路径中的资源 ID：`https://api.example.com/v1/objects/abcdef1234567890abcdef1234567890`
- `data:image/png;base64,iVBORw0KGgo...` 等内联资源
- JWT 形式的资源 ID（某些追踪 ID 也是三段 base64）
- 长 base64 字符串实际是序列化数据 / 协议 buffer / 图片二进制
- CSS / SVG / 字体文件中的 base64 内嵌

判定特征：
- 上下文为 URL 拼接、HTTP 请求路径、资源定位
- 周围有 `data:`、`base64,`、`url(`、`href=`、`src=`、`fetch(`、`request(` 等
- 字符串长度异常长（>200 字符）通常是内嵌数据而非凭证

#### 9. 代码 / 标识符被误识（重点）

值实际是代码片段、标识符、枚举值，被关键字误命中：

- 函数名 / 方法名包含 `password`、`secret` 等关键字（`getPassword()`、`encryptSecret()`），右"值"实际是参数列表或函数体
- 枚举常量名（`enum FieldType { PASSWORD, USERNAME }`）
- SQL 语句中的列名（`SELECT password FROM users WHERE id = ?`）
- 类型定义 / 接口字段名（`type Config struct { Password string \`json:"password"\` }`）
- 国际化资源 key（`i18n.t("login.password.placeholder")`）

#### 10. 已失效 / 已撤销的凭证

代码中明确注释 `// revoked`、`// deprecated`、`// 已禁用`、`// 已下线` 的凭证，且通过其他证据可确认（如对应账号已删除）。但**默认不应据此判 FP**，因为复核者通常无法验证撤销状态——保守判 [true_positive] 并提示用户清理。

### 关于测试 / Mock 数据的判定

**不将 test/mock 路径作为判定 FP 的依据。** 原因：

- 测试代码中残留真实凭证是常见安全事故（联调环境真 SK、CI 配置 token 等）
- 仅凭路径无法区分"为测试构造的 fake key"与"测试时临时使用真 key"
- 即使是测试代码，泄露真实凭证仍构成安全风险

**判定原则：** 即使漏洞位于 `test/`、`mock/`、`fixtures/`、`*_test.go`、`*Test.java` 等路径下，仍按上述 1-9 条规则正常判定。只有当值本身满足占位符、Hash、URL、代码片段等明确特征时才判 FP；若值符合真实凭证格式，则判 [true_positive]。

### 判断逻辑

1. 根据 `parsed_result.json` 中的 `file`、`startLine`、`endLine` 定位漏洞在源文件中的**实际位置**，读取该位置上下若干行。
2. 看右值是字面量字符串还是表达式：
   - 表达式（函数调用/变量/属性访问）→ [false_positive]
   - 字面量 → 进入下一步
3. 看字面量内容（重点关注规则层面无法区分的特征）：
   - 占位符 / 公开 demo key → [false_positive]
   - 哈希 / UUID / commit SHA / Trace ID → 结合变量名和上下文判断
   - URL 路径段 / base64 内嵌数据 / 长 base64 序列化 → [false_positive]
   - 函数名 / 枚举 / SQL 列名 / 字段定义被误命中 → [false_positive]
   - 符合云厂商真实凭证格式 → 进入下一步
4. 看上下文语义（不再以 test/mock 路径作判定依据）：
   - 文档 / 注释中明确标注为示例值 → [false_positive]
   - 业务代码 / 配置 / 测试代码中的字面量真实凭证 → [true_positive]
5. 任一环节存疑时，默认 [true_positive]。

### 反面案例（不应据此判 FP）

以下推理在历史复核中出现过，**全部判定错误**，复核时禁止采用：

- ❌ "`config_filter/test.properties` 第 57 行 `iam.password = 123@fjekk93feF13GGIAM` 是 Maven 构建过滤文件中的孤立属性条目，该 key 未被任何 application.yml 或 Java 源码中的 `${...}` 占位符引用（grep 确认零引用），且与其他环境共享同一基础值 `123@fjekk93feF13` 仅后缀不同，属于遗留的占位符/测试数据而非真实凭证。"
  - 错误点：(1) "grep 零引用"是对运行时使用情况的推断，不属于规则层面的 FP 特征；(2) "环境间共享前缀仅后缀不同"是命名习惯，不能证明非真实凭证；(3) 值 `123@fjekk93feF13GGIAM` 在源码中是字面量字符串赋值给 `iam.password`，符合真实凭证模式，应判 [true_positive]。

## 结果输出

将结果写入到 `analyze_report.json` 文件中，与 `parsed_result.json` 同目录。**输出必须是合法 JSON**（不允许 `//` 注释、不允许末尾逗号），可使用 `json.Valid` 或本地 `python -m json.tool` 自查。

字段说明：
- `vul_hash`：从 `parsed_result.json` 漏洞条目中的 `hash` 字段拷贝，用于回写关联。
- `result`：取值仅限 `"true_positive"` 或 `"false_positive"`。
- `reason`：必须包含三要素：（1）实际代码片段或其语义；（2）属于哪类误报/真报模式；（3）所依据的文件路径与行号。

文件示例（合法 JSON，可直接保存）：

```json
[
    {
        "vul_hash": "27632f7969b14b845061c9ff16a24233",
        "result": "false_positive",
        "reason": "conf/default.py:18 实际代码为 `password = os.getenv(\"DB_PASSWORD\", \"\")`，右值是环境变量读取而非凭证字面量，属于函数调用 / 环境变量读取误报。"
    },
    {
        "vul_hash": "faabde3378fa1a8288c6fa53b83e4665",
        "result": "true_positive",
        "reason": "conf/default.json:19 硬编码了 32 位十六进制的 sk 字面量，字段语义为密钥，未发现占位符、示例标注或运行时读取逻辑，符合百度云 SK 格式，属于真实凭证泄露。"
    },
    {
        "vul_hash": "1bf706dc4044f74dd75114ff0c28b9f1",
        "result": "false_positive",
        "reason": "docs/getting-started.md:42 的值 `AKIAIOSFODNN7EXAMPLE` 是 AWS 官方文档公开示例 AK，且位于明确示例代码块中，属于公开 Demo Key 误报。"
    }
]
```
