diff --git a/docs/supported-tech.md b/docs/supported-tech.md index 58bc428..d279ff9 100644 --- a/docs/supported-tech.md +++ b/docs/supported-tech.md @@ -160,8 +160,17 @@ dedicated matchers (gRPC service impl already partially covered). ## Rust +### Framework-gated matchers Detection emits tags (`actix`, `axum`, `rocket`, `warp`, `tide`, `poem`, -`tonic`, `lambda-rs`) but dedicated matchers are roadmap. +`tonic`, `lambda-rs`); the matching `rs-actix-route`, `rs-axum-route`, +`rs-rocket-route`, `rs-warp-filter`, `rs-tide-route`, `rs-poem-route`, +`rs-tonic-grpc`, and `rs-lambda-runtime` matchers activate only when +their tag is detected. + +### Generic Rust (`rust`) +Always-on Rust matchers regardless of framework: `rs-sql-raw`, +`rs-command-injection`, `rs-path-traversal`, `rs-ssrf`, +`rs-tls-no-verify`, `rs-untrusted-deserialization`. ## JVM (Java / Kotlin) diff --git a/packages/scanner/src/matchers/index.ts b/packages/scanner/src/matchers/index.ts index e86ba78..a08d7b8 100644 --- a/packages/scanner/src/matchers/index.ts +++ b/packages/scanner/src/matchers/index.ts @@ -168,12 +168,17 @@ import { rceMatcher } from "./rce.js"; import { responseHeaderLeakMatcher } from "./response-header-leak.js"; import { rsActixRouteMatcher } from "./rs-actix-route.js"; import { rsAxumRouteMatcher } from "./rs-axum-route.js"; +import { rsCommandInjectionMatcher } from "./rs-command-injection.js"; import { rsLambdaRuntimeMatcher } from "./rs-lambda-runtime.js"; +import { rsPathTraversalMatcher } from "./rs-path-traversal.js"; import { rsPoemRouteMatcher } from "./rs-poem-route.js"; import { rsRocketRouteMatcher } from "./rs-rocket-route.js"; import { rsSqlRawMatcher } from "./rs-sql-raw.js"; +import { rsSsrfMatcher } from "./rs-ssrf.js"; import { rsTideRouteMatcher } from "./rs-tide-route.js"; +import { rsTlsNoVerifyMatcher } from "./rs-tls-no-verify.js"; import { rsTonicGrpcMatcher } from "./rs-tonic-grpc.js"; +import { rsUntrustedDeserializationMatcher } from "./rs-untrusted-deserialization.js"; import { rsWarpFilterMatcher } from "./rs-warp-filter.js"; import { sandboxRuntimeScriptMatcher } from "./sandbox-runtime-script.js"; import { secretEnvVarMatcher } from "./secret-env-var.js"; @@ -424,6 +429,11 @@ export function createDefaultRegistry(): MatcherRegistry { registry.register(rsPoemRouteMatcher); registry.register(rsTonicGrpcMatcher); registry.register(rsLambdaRuntimeMatcher); + registry.register(rsCommandInjectionMatcher); + registry.register(rsPathTraversalMatcher); + registry.register(rsSsrfMatcher); + registry.register(rsTlsNoVerifyMatcher); + registry.register(rsUntrustedDeserializationMatcher); // JVM registry.register(jvmSpringControllerMatcher); registry.register(jvmKtorRouteMatcher); diff --git a/packages/scanner/src/matchers/rs-command-injection.ts b/packages/scanner/src/matchers/rs-command-injection.ts new file mode 100644 index 0000000..6ec3469 --- /dev/null +++ b/packages/scanner/src/matchers/rs-command-injection.ts @@ -0,0 +1,44 @@ +import type { MatcherPlugin } from "../types.js"; +import { regexMatcher } from "./utils.js"; + +export const rsCommandInjectionMatcher: MatcherPlugin = { + noiseTier: "precise" as const, + slug: "rs-command-injection", + description: "Rust std::process / tokio::process Command with potentially dynamic arguments", + filePatterns: ["**/*.rs"], + requires: { tech: ["rust"] }, + examples: [ + `let output = std::process::Command::new("ls").arg(dir).output()?;`, + `let child = std::process::Command::new("git").arg("clone").arg(repo_url).spawn()?;`, + `tokio::process::Command::new("sh").arg("-c").arg(user_input).status().await?;`, + `let child = process::Command::new("rsync").arg(src).arg(dst).spawn()?;`, + `Command::new("bash").arg("-c").arg(cmd).output()?;`, + `let out = std::process::Command::new("pwsh").args(&args).output()?;`, + ], + match(content, filePath) { + if (/\/(tests|examples|benches)\//.test(filePath)) return []; + + return regexMatcher( + "rs-command-injection", + [ + { + regex: /\bstd::process::Command::new\s*\(/, + label: "std::process::Command::new", + }, + { + regex: /\btokio::process::Command::new\s*\(/, + label: "tokio::process::Command::new", + }, + { + regex: /(?