JasminGuberinic/kotlin-security-scanner

GitHub: JasminGuberinic/kotlin-security-scanner

一款 Detekt 插件,在编译时和 IDE 中对 Kotlin Spring Boot 等框架的代码进行 OWASP Top 10 安全漏洞静态扫描。

Stars: 0 | Forks: 0

# kotlin-security-scanner [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/cc8bad3018001631.svg)](https://github.com/JasminGuberinic/kotlin-security-scanner/actions/workflows/ci.yml) [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) [![Kotlin](https://img.shields.io/badge/kotlin-2.0.10-purple.svg)](https://kotlinlang.org) [![Detekt](https://img.shields.io/badge/detekt-1.23.7-blue.svg)](https://detekt.dev) [![Rules](https://img.shields.io/badge/rules-35%2B-brightgreen.svg)](#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, 后台面板检测, 安全专业人员, 安全扫描, 时序注入, 错误基检测, 静态代码分析