JasminGuberinic/kotlin-security-scanner
GitHub: JasminGuberinic/kotlin-security-scanner
一款 Detekt 插件,在编译时和 IDE 中对 Kotlin Spring Boot 等框架的代码进行 OWASP Top 10 安全漏洞静态扫描。
Stars: 0 | Forks: 0
# kotlin-security-scanner
[](https://github.com/JasminGuberinic/kotlin-security-scanner/actions/workflows/ci.yml)
[](LICENSE)
[](https://kotlinlang.org)
[](https://detekt.dev)
[](#owasp-top-10-coverage)
**一款 Detekt 插件,可在编译时、在您的 IDE 中,以零基础设施的代价,捕获 Kotlin Spring Boot、Quarkus 和 Dropwizard 应用程序中的 OWASP Top 10 安全漏洞。**
## 快速开始
```
// build.gradle.kts
plugins {
id("io.gitlab.arturbosch.detekt") version "1.23.7"
}
dependencies {
// Core rules — works with any Kotlin project
detektPlugins(files("path/to/scanner-core-0.1.0-SNAPSHOT.jar"))
// Pick ONE framework module (or use scanner-all for everything)
detektPlugins(files("path/to/scanner-spring-boot-0.1.0-SNAPSHOT.jar"))
// detektPlugins(files("path/to/scanner-quarkus-0.1.0-SNAPSHOT.jar"))
// detektPlugins(files("path/to/scanner-dropwizard-0.1.0-SNAPSHOT.jar"))
}
detekt {
config.setFrom(files("config/detekt/detekt.yml"))
buildUponDefaultConfig = true
}
```
构建 JAR 文件:
```
git clone https://github.com/JasminGuberinic/kotlin-security-scanner.git
cd kotlin-security-scanner
export JAVA_HOME=/opt/homebrew/opt/openjdk@21 # or your JDK 21 path
./gradlew assemble
# JARs → scanner-*/build/libs/
```
运行:
```
./gradlew detekt
```
## GitHub 代码扫描 (SARIF)
Detekt 原生输出 SARIF。将以下内容放入 `.github/workflows/security.yml`:
```
- uses: actions/checkout@v4
- name: Run Kotlin security scan
run: ./gradlew detekt
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: build/reports/detekt/
```
结果将直接显示在 Pull Request 的差异中——无需账户、无需服务器、无需成本(对公开仓库免费)。
## OWASP Top 10 覆盖范围
跨 4 个模块的 35 条规则。每条规则都包含正向、反向和跨规则隔离测试。
### Core — 任何 Kotlin 项目 (`scanner-core`)
| 规则 | OWASP 2021 | 捕获内容 |
|---|---|---|
| `WeakCipherModeRule` | A02 | `Cipher.getInstance()` 中的 ECB mode、DES、RC4、RC2、Blowfish |
| `WeakHashAlgorithmRule` | A02 | `MessageDigest.getInstance("MD5"\|"SHA-1")` |
| `TrustAllCertsRule` | A02 | 空的 `X509TrustManager` —— 接受任何证书 |
| `HardcodedIvRule` | A02 | 带有硬编码字节的 `IvParameterSpec(byteArrayOf(...))` |
| `WeakRsaKeyRule` | A02 | `KeyPairGenerator.initialize(≤1024)` |
| `SqlInjectionRule` | A03 | SQL 查询中的字符串插值或 `+` |
| `LdapInjectionRule` | A03 | `ctx.search()` / `ctx.bind()` 中的动态字符串 |
| `JndiInjectionRule` | A03 | `ctx.lookup(nonLiteral)` —— 远程 JNDI 代码执行 |
| `XpathInjectionRule` | A03 | `xpath.evaluate()` 中的动态 XPath 表达式 |
| `ReflectionInjectionRule` | A03 | `Class.forName(nonLiteral)` —— 加载攻击者指定的类 |
| `PathTraversalRule` | A03 | 带有非字面量输入的 `File()` / `Paths.get()` |
| `CommandInjectionRule` | A03 | 带有动态参数的 `Runtime.exec()` / `ProcessBuilder()` |
| `XxeInjectionRule` | A03 | 未禁用 DTD 的 `DocumentBuilderFactory.newInstance()` |
| `GroovyScriptInjectionRule` | A03 | `GroovyShell().evaluate(nonLiteral)` —— 脚本 RCE |
| `HardcodedCredentialsRule` | A07 | 源代码中硬编码的密码、API key、token |
| `InsecureRandomRule` | A07 | 用于安全值的 `java.util.Random` / `ThreadLocalRandom` |
| `InsecureDeserializationRule` | A08 | `ObjectInputStream` —— 对不受信任的数据不安全 |
| `SensitiveDataLoggingRule` | A09 | 插入到日志语句中的密码或 token |
| `SsrfRule` | A10 | 由非字面量值构建的 `URL()` / `URI()` |
### Spring Boot (`scanner-spring-boot`)
| 规则 | OWASP 2021 | 捕获内容 |
|---|---|---|
| `MissingAuthorizationRule` | A01 | 没有 `@PreAuthorize` 或 `@Secured` 的 `@GetMapping` 等 |
| `DisabledHttpSecurityRule` | A01 | `SecurityFilterChain` 中的 `anyRequest().permitAll()` |
| `OpenRedirectRule` | A01 | `@Controller` 方法中的 `"redirect:" + variable` |
| `CsrfTokenLeakRule` | A01 | 暴露 CSRF token 的 `model.addAttribute("csrf/xsrf...", token)` |
| `InsecurePasswordEncoderRule` | A02 | `NoOpPasswordEncoder`、`Md5PasswordEncoder` |
| `SpelInjectionRule` | A03 | `parseExpression(nonLiteral)` —— SpEL RCE |
| `ResponseSplittingRule` | A03 | `response.addHeader(name, nonLiteral)` —— CR/LF injection |
| `EL InjectionRule` | A03 | `ELProcessor.eval(nonLiteral)` —— EL 表达式 RCE |
| `MassAssignmentRule` | A04 | JPA `@Entity` 类上的 `@RequestBody` |
| `SpringCsrfDisabledRule` | A05 | `.csrf { disable() }` / `.csrf().disable()` |
| `PermissiveCorsRule` | A05 | CORS 配置中的 `allowedOrigins("*")` |
### Quarkus (`scanner-quarkus`)
| 规则 | OWASP 2021 | 捕获内容 |
|---|---|---|
| `QuarkusMissingAuthRule` | A01 | 没有 `@RolesAllowed` / `@Authenticated` 的 JAX-RS `@GET` 等 |
| `QuarkusPermitAllSensitiveRule` | A01 | `@DELETE` / `@PUT` endpoint 上的 `@PermitAll` |
| `PanacheRawQueryRule` | A03 | `PanacheEntity.find(interpolated)` —— NoSQL/ORM injection |
| `QuarkusUnsafeHeaderRule` | A05 | `Response.header(name, nonLiteral)` —— response splitting |
| `QuarkusHardcodedConfigSecretRule` | A07 | `@ConfigProperty(defaultValue="hardcoded-secret")` |
| `QuarkusReflectionUnsafeRule` | A08 | 带有 `readObject` 的 `Serializable` 上的 `@RegisterForReflection` |
### Dropwizard (`scanner-dropwizard`)
| 规则 | OWASP 2021 | 捕获内容 |
|---|---|---|
| `DropwizardMissingAuthRule` | A01 | 没有 `@RolesAllowed`、`@DenyAll` 或 `@Auth` 的 JAX-RS `@GET` 等 |
| `DropwizardOpenRedirectRule` | A01 | `Response.seeOther(URI(variable))` —— open redirect |
| `InsecureTlsProtocolRule` | A02 | TLS 配置中的 TLS 1.0、TLS 1.1、SSLv2、SSLv3 |
| `InsecureCookieRule` | A05 | 没有 `secure=true` 的 `NewCookie(name, value)` |
## 为什么不用 FindSecBugs / SonarQube?
| 功能 | FindSecBugs | SonarQube | **kotlin-security-scanner** |
|---|---|---|---|
| Kotlin 原生 (PSI/AST) | ❌ 仅限 Bytecode | ⚠️ 部分支持 | ✅ |
| Coroutine 安全模式 | ❌ 无法实现 | ❌ | ✅ |
| `let`/`run`/`apply` 污染追踪 | ❌ | ❌ | ✅ |
| Spring Boot / Quarkus / Dropwizard 规则 | ⚠️ 仅限 Java | ⚠️ 付费版 | ✅ |
| 配置文件扫描 (`.properties`/`.yml`) | ❌ | ❌ | ✅(开发中) |
| 需要的基础设施 | ❌ | ✅ 服务器 | ❌ |
| 成本 | 免费 | 免费 / 付费 | 免费 |
| IDE 集成 (Detekt) | ❌ | ❌ | ✅ |
FindSecBugs 会遗漏 Kotlin 特有的模式,原因如下:
- Kotlin 作用域函数(`let`、`run`、`apply`、`also`)会编译成 lambda bytecode,从而破坏污染分析
- `suspend fun` —— coroutine desugaring 使得授权注解在 bytecode 层面不可见
- Kotlin 的 PSI 节点(`KtStringTemplateExpression`、`KtDotQualifiedExpression`)没有直接的 bytecode 等价物
参见 [FindSecBugs issue #432](https://github.com/find-sec-bugs/find-sec-bugs/issues/432) 和 [Kotlin taint analysis limitations](https://arxiv.org/abs/2207.09379)。
## 输出示例
```
src/main/kotlin/com/example/UserService.kt:42:9
String interpolation in SQL — use :namedParam or ? placeholders [SqlInjection]
src/main/kotlin/com/example/AuthConfig.kt:18:5
anyRequest().permitAll() makes all endpoints public [DisabledHttpSecurity]
src/main/kotlin/com/example/CryptoService.kt:31:9
AES/ECB — use AES/GCM/NoPadding instead [WeakCipherMode]
src/main/kotlin/com/example/JwtConfig.kt:12:5
signWith(NONE) disables JWT signature — use HS256/RS256 [JwtNoneAlgorithm]
src/main/kotlin/com/example/UserController.kt:25:5
suspend fun with @PreAuthorize — security context is silently dropped [CoroutineSecurityContextLoss]
```
## 架构
多模块设计——只需添加您的项目使用的模块。
```
kotlin-security-scanner/
├── scanner-core/ # Framework-agnostic rules (any Kotlin project)
│ └── core/
│ ├── SecurityRule.kt # Base class — reportAt() helper
│ └── DetectionPatterns.kt # All patterns, single source of truth
│
├── scanner-spring-boot/ # Spring MVC / Spring Security rules
├── scanner-quarkus/ # Quarkus / MicroProfile / JAX-RS rules
├── scanner-dropwizard/ # Dropwizard / JAX-RS rules
└── scanner-all/ # Convenience bundle (all modules)
```
设计原则:
- 每条规则都位于与其类别匹配的 OWASP package 中(`a01/`–`a10/`)
- 所有模式都放在 `DetectionPatterns.kt` 中——规则从不硬编码字符串
- 规则之间没有耦合——每条规则只从 `core/` 导入
- 每条规则有三层测试:正向(标记)、反向(安全)、隔离(其他规则的 fixture)
## 添加规则
该项目使用 Claude Code skill 来确保规则贡献的一致性:
```
/add-security-rule WeakRsaKeyRule core A02
```
该 skill 会读取 `RULES_BACKLOG.md` 中预先调研好的 backlog,生成规则类、测试类,在 provider 和 `detekt.yml` 中进行注册,并更新覆盖范围表——所有这些都遵循既定的模式。
PR 的手动检查清单:
- [ ] 规则位于正确模块中正确的 `a0X/` package 中
- [ ] 在规则的 KDoc 中引用了 FindSecBugs ID
- [ ] 模式已添加到 `DetectionPatterns.kt`
- [ ] 测试:≥3 个正向、≥2 个反向、1 个隔离
- [ ] `./gradlew test detekt` 顺利通过
## 技术栈
| 组件 | 版本 |
|---|---|
| Kotlin | 2.0.10 |
| Detekt | 1.23.7 |
| Java | 21 |
| Gradle | 8.x |
| AssertJ | 3.26.3 |
## 许可证
Apache License 2.0 —— 与 Spring Boot 和 Detekt 使用相同的 License。
*受 [FindSecBugs](https://find-sec-bugs.github.io)、[gosec](https://github.com/securego/gosec) 和 [OWASP Top 10](https://owasp.org/Top10/) 的启发。*
标签:Detekt插件, JS文件枚举, Kotlin, OWASP Top 10, 后台面板检测, 安全专业人员, 安全扫描, 时序注入, 错误基检测, 静态代码分析