AYAN-AMBESH/javscan
GitHub: AYAN-AMBESH/javscan
一款用 Rust 编写的高性能 Java 静态安全测试工具,通过基于 AST 的污点分析精确检测 28 类代码安全漏洞。
Stars: 0 | Forks: 0
# javscan
**高级 Java 静态应用安全测试 (SAST),使用 Rust 编写。**
javscan 使用真实的 AST (tree-sitter) 解析 Java,并运行过程内**污点分析**,以追踪用户可控数据从 source(servlet 请求访问器、Spring MVC 注解参数、`main(String[] args)`、stdin)经由赋值、字符串拼接、`StringBuilder` 和 `String` 转换,最终流入危险的 sink —— 同时识别 sanitizer(OWASP Java Encoder、Apache commons-text 转义、`URLEncoder`、数字解析、`UUID.fromString`、`FilenameUtils.getName`),以降低误报率。
[sapyscan](https://github.com/AYAN-AMBESH/sapyscan) (Python SAST) 的姊妹项目。
## 亮点
- **28 条安全规则**,附带 CWE 和 OWASP Top 10 (2021) 映射
- **基于 AST 的污点追踪**,而非正则表达式匹配 —— 带有绑定参数的 `PreparedStatement` 或 `Encode.forHtml()` 调用不会产生噪音
- **快速**:通过 rayon 进行并行扫描;约 6 万行 Java 代码只需约一秒钟
- **4 种输出格式**:彩色控制台、JSON、**SARIF 2.1.0**(兼容 GitHub Code Scanning)和自包含的交互式 **HTML 仪表板**
- **基线对比**:对现有发现进行快照,然后仅针对*新*问题导致 CI 失败(指纹在行号变动后依然有效)
- **内联抑制** (`// nosec`, `// javscan:ignore JS101`)、项目配置文件、严重性/置信度级别、可配置的退出代码
- 自动跳过测试源码 (`src/test`, `*Test.java`) 和构建/生成的目录
- **类型感知的 sink 匹配**和**信任边界标记**(见下文)—— 危险的*名称*不会被视为危险的*调用*
## 精确度:类型解析和信任边界
仅基于名称的匹配是 SAST 误报的主要来源。javscan 在报告之前会解析三件事:
**1. 接收者的类型,而非其名称。** `.load()` 仅在接收者实际解析为
`org.yaml.snakeyaml.Yaml` 时才会触发。强化包装器
(`YamlProcessor`, `YamlPropertySourceLoader` 及其子类) 使用 `SafeConstructor`
构建其解析器,并被列入白名单。构造点会按变量进行检查,因此
`new Yaml(new SafeConstructor(...))` 是静默的,而
`new Yaml(new Constructor(...))` 是 CRITICAL。如果 `pom.xml`/`build.gradle`
声明了 SnakeYAML >= 2.0 —— 默认拒绝全局类型标签 —— 那么单纯的
`new Yaml()` 会降级为 INFO。
**2. 方法的所有者,而非其名称。**
`SecurityJackson2Modules.enableDefaultTyping(mapper)` 安装了白名单
`PolymorphicTypeValidator`;它是与
`ObjectMapper#enableDefaultTyping` 不同的符号,因此不会被报告。
带有真实验证器的 `activateDefaultTyping(...)` 是 INFO(“确认白名单范围”);
而使用 `LaissezFaireSubTypeValidator` 则是 CRITICAL。
**3. 值,而非标识符。** `hardcoded-secret` 要求*值*看起来像密钥材料。
片段表示角色的名称 —— `*_TYPE`, `*_PREFIX`, `*_REGEX`, `*_REASON`,
`*_HEADER`, …… —— 是领域名词,而不是凭证声明。URI、正则表达式、
命名空间前缀、标签词汇
(`Opaque`, `Bearer`) 和小写单词短语无论其熵值如何都会被拒绝;
长高熵字符串和混合字符类密码将被报告。
**信任来源。** 每个发现都带有一个 `source` 标签 ——
`NETWORK`、`LOCAL_FS`、`PLUGIN`、`THEME`、`CONFIG`、`CONSTANT` 或 `UNKNOWN`。
只有 `NETWORK`(以及 `UNKNOWN`,它可能仍然是网络)保留完全的严重性;
其他级别的严重性会下降两到三级,并附带说明解释原因。一个危险的
API 读取同一个类通过 `ObjectOutputStream` 序列化的文件属于自序列化往返:
仍然会被报告,但级别为 LOW,因为要对其进行投毒需要先获得本地写入权限。针对
`SimpleEvaluationContext` 评估的 SpEL —— 它禁止了 SpEL RCE 所需的 `T(...)`
类型引用和构造函数调用 —— 完全不会被报告。
`tests/regression.rs` 将这些分别固定为正面和负面的黄金测试夹具,
因此精确度修复不会无声无息地变成召回率回归。
## 规则
| ID | 规则 | 严重性 | CWE |
|----|------|----------|-----|
| JS101 | sql-injection | CRITICAL | CWE-89 |
| JS102 | command-injection | CRITICAL | CWE-78 |
| JS103 | ldap-injection | HIGH | CWE-90 |
| JS104 | xpath-injection | HIGH | CWE-643 |
| JS105 | spel-injection | CRITICAL | CWE-917 |
| JS106 | script-engine-injection | CRITICAL | CWE-94 |
| JS107 | log-injection | LOW | CWE-117 |
| JS201 | cross-site-scripting | HIGH | CWE-79 |
| JS202 | open-redirect | MEDIUM | CWE-601 |
| JS203 | ssrf | HIGH | CWE-918 |
| JS204 | insecure-cookie | MEDIUM | CWE-614 |
| JS205 | csrf-disabled | HIGH | CWE-352 |
| JS206 | permissive-cors | MEDIUM | CWE-942 |
| JS207 | cleartext-http | LOW | CWE-319 |
| JS301 | weak-hash (MD5/SHA-1) | HIGH | CWE-328 |
| JS302 | weak-cipher (DES/RC4/ECB) | HIGH | CWE-327 |
| JS303 | weak-random | LOW–HIGH | CWE-330 |
| JS304 | hardcoded-crypto-material | HIGH | CWE-321 |
| JS305 | trust-all-tls | CRITICAL | CWE-295 |
| JS306 | weak-key-size | MEDIUM | CWE-326 |
| JS401 | hardcoded-secret | HIGH–CRITICAL | CWE-798 |
| JS402 | jwt-weak-signing | HIGH | CWE-347 |
| JS501 | insecure-deserialization | CRITICAL | CWE-502 |
| JS502 | xxe | HIGH | CWE-611 |
| JS601 | path-traversal | HIGH | CWE-22 |
| JS602 | zip-slip | HIGH | CWE-22 |
| JS701 | redos | MEDIUM | CWE-1333 |
| JS702 | unsafe-reflection | HIGH | CWE-470 |
运行 `javscan --rules` 获取完整描述。
## 安装
```
cargo install --path .
# 或
cargo build --release # binary at target/release/javscan
```
## 用法
```
# 扫描项目(控制台输出)
javscan src/
# 交互式 HTML 仪表板
javscan . --format html --output report.html
# 用于 GitHub Code Scanning 的 SARIF
javscan . --format sarif --output results.sarif
# 机器可读的 JSON
javscan . --format json --output results.json
# 仅包含高和严重级别的发现;遇到严重级别时使 CI 失败
javscan . --min-severity high --fail-on critical
# 禁用规则,排除路径,也扫描测试
javscan . --disable JS207 --disable log-injection --exclude "**/generated/**" --include-tests
```
### 基线工作流(在现有代码库上采用 javscan)
```
javscan . --write-baseline .javscan-baseline.json # accept current findings
javscan . --baseline .javscan-baseline.json # from now on: only NEW findings
```
指纹对规则、文件和规范化的代码行进行哈希处理 —— 而不是行号 —— 因此不相关的编辑不会让基线化的发现重新出现。
### 内联抑制
```
Runtime.getRuntime().exec(cmd); // nosec
stmt.executeQuery(sql); // javscan:ignore JS101
// javscan:ignore JS102,JS601 (a comment on the line above also applies)
```
### 配置 —— 位于项目根目录的 `.javscan.json`
```
{
"exclude": ["**/generated/**", "**/legacy/**"],
"disabled_rules": ["JS207"],
"min_severity": "low",
"include_tests": false,
"fail_on": "high"
}
```
CLI 参数会覆盖配置文件。
### 退出代码
- `0` — 没有达到或超过 `--fail-on` 阈值(默认为 `high`)的发现;使用 `--fail-on never` 可始终以 0 退出
- `1` — 存在达到/超过阈值的发现
- `2` — 执行错误(基线损坏、输出不可写等)
## 污点分析的工作原理
1. **Source** 播种污点:带有 `@RequestParam` / `@PathVariable` / `@RequestBody` / `@RequestHeader` / `@CookieValue` 注解的参数,`@GetMapping` 风格处理程序和 servlet `doGet`/`doPost` 的参数,`HttpServletRequest` 访问器 (`getParameter`, `getHeader`, `getQueryString`, ...),`main` 参数,`Scanner`/`BufferedReader` 读取。
2. **传播**会按方法运行到不动点:赋值、`+` 拼接、`StringBuilder.append/insert`、`String.format/valueOf/substring/replace/...`、三元运算、类型转换、对受污染集合的增强 for 循环,以及(启发式地)接受受污染参数的调用。
3. **Sanitizer** 清除污点:OWASP `Encode.forHtml/forJavaScript/...`、ESAPI 编码器、commons-text `escapeHtml4/escapeXml/...`、`URLEncoder.encode`、`Integer.parseInt` 及其同类、`UUID.fromString`、`FilenameUtils.getName`、`Pattern.quote`。
4. **Sink** 是特定于规则的(SQL 执行、`Runtime.exec`、响应写入器、文件构造器等...)。受污染的 sink 参数会产生高置信度的发现;动态但未经验证的拼接会在降低严重性的情况下产生中/低置信度的发现。
分析是过程内的(按方法),带有启发式调用传播 —— 这是在高扫描速度下 SAST 精确度/召回率的经典权衡。
污点分析回答的是*“这是否受攻击者控制?”*,这并不等同于*“这是否安全?”*
—— 一个未受污染的值可能仍然来自本地文件、插件
描述符或主题模板。对于无论调用者如何 sink 都是危险的规则(原生反序列化、
SpEL、反射),信任来源分类器填补了这一空白,
因此这些发现是根据可达性进行排名,而不是被抑制或留在 CRITICAL 级别。
## 示例
```
examples/vulnerable/UserController.java
32:13 CRITICAL JS101 [sql-injection] (high confidence, CWE-89)
User-controlled data reaches SQL sink `executeQuery()`; the query is attacker-influenced.
> stmt.executeQuery(query);
```
`examples/` 目录包含故意设置的易受攻击和干净的文件,用作验收语料库;干净文件会产生零发现。
## 许可证
MIT
标签:DevSecOps, LNA, Rust, SAST, 上游代理, 可视化界面, 盲注攻击, 网络流量审计, 通知系统, 静态应用安全测试