diff --git a/detect/threat_test.go b/detect/threat_test.go index a6c3f52..a4a809e 100644 --- a/detect/threat_test.go +++ b/detect/threat_test.go @@ -258,26 +258,38 @@ func TestSinksRubyProject(t *testing.T) { sr := engine.Sinks(r) if len(sr.Sinks) == 0 { - t.Fatal("expected sinks from Ruby language def") + t.Fatal("expected sinks from detected tools") } - // All sinks in this fixture come from Ruby (only ruby/language.toml has sinks). - bySymbol := make(map[string]brief.SinkEntry) + // Index by tool+symbol since multiple tools can have a sink with the same name. + type key struct{ tool, symbol string } + idx := make(map[key]brief.SinkEntry) for _, s := range sr.Sinks { - if s.Tool != "Ruby" { - t.Errorf("unexpected sink from %q: %v", s.Tool, s) - } - bySymbol[s.Symbol] = s + idx[key{s.Tool, s.Symbol}] = s } - if e, ok := bySymbol["eval"]; !ok { - t.Error("expected eval sink") + // Ruby stdlib sinks + if e, ok := idx[key{"Ruby", "eval"}]; !ok { + t.Error("expected Ruby eval sink") } else if e.Threat != "code_injection" || e.CWE != "CWE-95" { - t.Errorf("eval sink = %+v", e) + t.Errorf("Ruby eval sink = %+v", e) + } + if _, ok := idx[key{"Ruby", "Marshal.load"}]; !ok { + t.Error("expected Ruby Marshal.load sink") + } + + // Rails framework sinks + if e, ok := idx[key{"Rails", "html_safe"}]; !ok { + t.Error("expected Rails html_safe sink") + } else if e.Threat != "xss" { + t.Errorf("Rails html_safe threat = %q, want xss", e.Threat) } - if _, ok := bySymbol["Marshal.load"]; !ok { - t.Error("expected Marshal.load sink") + // ActiveRecord ORM sinks + if e, ok := idx[key{"ActiveRecord", "find_by_sql"}]; !ok { + t.Error("expected ActiveRecord find_by_sql sink") + } else if e.Threat != "sql_injection" { + t.Errorf("ActiveRecord find_by_sql threat = %q, want sql_injection", e.Threat) } } diff --git a/knowledge/elixir/ecto.toml b/knowledge/elixir/ecto.toml index 782cdef..f649856 100644 --- a/knowledge/elixir/ecto.toml +++ b/knowledge/elixir/ecto.toml @@ -20,3 +20,31 @@ run = "mix ecto.migrate" role = ["library"] function = ["data-mapping", "validation"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "Repo.query" +threat = "sql_injection" +cwe = "CWE-89" +note = "With interpolation; $1 placeholders are safe" + +[[security.sinks]] +symbol = "Repo.query!" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "fragment" +threat = "sql_injection" +cwe = "CWE-89" +note = "With ^ pinned interpolation outside the fragment" + +[[security.sinks]] +symbol = "Ecto.Adapters.SQL.query" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "cast" +threat = "mass_assignment" +cwe = "CWE-915" +note = "Without explicit field allowlist as second arg" diff --git a/knowledge/elixir/phoenix.toml b/knowledge/elixir/phoenix.toml index 878c5b1..b882145 100644 --- a/knowledge/elixir/phoenix.toml +++ b/knowledge/elixir/phoenix.toml @@ -23,3 +23,31 @@ role = ["framework"] function = ["api-development", "templating"] layer = ["backend", "full-stack"] domain = ["web-development"] + +[[security.sinks]] +symbol = "raw" +threat = "xss" +cwe = "CWE-79" +note = "Phoenix.HTML.raw bypasses escaping" + +[[security.sinks]] +symbol = "safe_to_string" +threat = "xss" +cwe = "CWE-79" + +[[security.sinks]] +symbol = "redirect" +threat = "open_redirect" +cwe = "CWE-601" +note = "With external: target" + +[[security.sinks]] +symbol = "send_file" +threat = "path_traversal" +cwe = "CWE-22" +note = "Plug.Conn.send_file" + +[[security.sinks]] +symbol = "send_download" +threat = "path_traversal" +cwe = "CWE-22" diff --git a/knowledge/go/echo.toml b/knowledge/go/echo.toml index fdffecf..91c869c 100644 --- a/knowledge/go/echo.toml +++ b/knowledge/go/echo.toml @@ -18,3 +18,29 @@ role = ["framework"] function = ["api-development"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "c.HTML" +threat = "xss" +cwe = "CWE-79" +note = "Renders raw HTML string" + +[[security.sinks]] +symbol = "c.HTMLBlob" +threat = "xss" +cwe = "CWE-79" + +[[security.sinks]] +symbol = "c.Redirect" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "c.File" +threat = "path_traversal" +cwe = "CWE-22" + +[[security.sinks]] +symbol = "c.Attachment" +threat = "path_traversal" +cwe = "CWE-22" diff --git a/knowledge/go/ent.toml b/knowledge/go/ent.toml index dd44e03..0490797 100644 --- a/knowledge/go/ent.toml +++ b/knowledge/go/ent.toml @@ -21,3 +21,15 @@ files = ["ent/schema/"] [taxonomy] function = ["code-generation"] + +[[security.sinks]] +symbol = "sql.Raw" +threat = "sql_injection" +cwe = "CWE-89" +note = "Generated code is safe; raw escape hatch is not" + +[[security.sinks]] +symbol = "ExecContext" +threat = "sql_injection" +cwe = "CWE-89" +note = "Direct driver access" diff --git a/knowledge/go/fiber.toml b/knowledge/go/fiber.toml index 7bb9a14..52cd5e4 100644 --- a/knowledge/go/fiber.toml +++ b/knowledge/go/fiber.toml @@ -18,3 +18,24 @@ role = ["framework"] function = ["api-development"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "c.SendString" +threat = "xss" +cwe = "CWE-79" +note = "When Content-Type is text/html" + +[[security.sinks]] +symbol = "c.Redirect" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "c.SendFile" +threat = "path_traversal" +cwe = "CWE-22" + +[[security.sinks]] +symbol = "c.Download" +threat = "path_traversal" +cwe = "CWE-22" diff --git a/knowledge/go/gin.toml b/knowledge/go/gin.toml index e587aba..afeabb2 100644 --- a/knowledge/go/gin.toml +++ b/knowledge/go/gin.toml @@ -17,3 +17,30 @@ role = ["framework"] function = ["api-development"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "c.String" +threat = "xss" +cwe = "CWE-79" +note = "No HTML escaping; use c.HTML for templated output" + +[[security.sinks]] +symbol = "c.Data" +threat = "xss" +cwe = "CWE-79" +note = "When Content-Type is text/html" + +[[security.sinks]] +symbol = "c.Redirect" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "c.File" +threat = "path_traversal" +cwe = "CWE-22" + +[[security.sinks]] +symbol = "c.FileAttachment" +threat = "path_traversal" +cwe = "CWE-22" diff --git a/knowledge/go/gorm.toml b/knowledge/go/gorm.toml index 85b5f7e..1fef5c6 100644 --- a/knowledge/go/gorm.toml +++ b/knowledge/go/gorm.toml @@ -16,3 +16,42 @@ ecosystems = ["go"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "Raw" +threat = "sql_injection" +cwe = "CWE-89" +note = "db.Raw with Sprintf; placeholders are safe" + +[[security.sinks]] +symbol = "Exec" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "Where" +threat = "sql_injection" +cwe = "CWE-89" +note = "With Sprintf string; struct/map forms are safe" + +[[security.sinks]] +symbol = "Order" +threat = "sql_injection" +cwe = "CWE-89" +note = "Column name not parameterizable" + +[[security.sinks]] +symbol = "Select" +threat = "sql_injection" +cwe = "CWE-89" +note = "With string from user input" + +[[security.sinks]] +symbol = "Group" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "Having" +threat = "sql_injection" +cwe = "CWE-89" diff --git a/knowledge/go/pgx.toml b/knowledge/go/pgx.toml index b75f72f..7491752 100644 --- a/knowledge/go/pgx.toml +++ b/knowledge/go/pgx.toml @@ -14,3 +14,19 @@ ecosystems = ["go"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "Query" +threat = "sql_injection" +cwe = "CWE-89" +note = "When query built via Sprintf; use $1 placeholders" + +[[security.sinks]] +symbol = "QueryRow" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "Exec" +threat = "sql_injection" +cwe = "CWE-89" diff --git a/knowledge/go/sqlx.toml b/knowledge/go/sqlx.toml index cb51053..5a437b6 100644 --- a/knowledge/go/sqlx.toml +++ b/knowledge/go/sqlx.toml @@ -14,3 +14,29 @@ ecosystems = ["go"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "Query" +threat = "sql_injection" +cwe = "CWE-89" +note = "When query built via Sprintf; use bindvars" + +[[security.sinks]] +symbol = "Queryx" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "Exec" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "Get" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "Select" +threat = "sql_injection" +cwe = "CWE-89" diff --git a/knowledge/java/spring-boot.toml b/knowledge/java/spring-boot.toml index 9374e9b..999ae4d 100644 --- a/knowledge/java/spring-boot.toml +++ b/knowledge/java/spring-boot.toml @@ -18,3 +18,50 @@ role = ["framework"] function = ["api-development", "dependency-injection", "data-mapping"] layer = ["backend", "full-stack"] domain = ["web-development"] + +[[security.sinks]] +symbol = "@ResponseBody" +threat = "xss" +cwe = "CWE-79" +note = "When returning HTML string without escaping" + +[[security.sinks]] +symbol = "th:utext" +threat = "xss" +cwe = "CWE-79" +note = "Thymeleaf unescaped text" + +[[security.sinks]] +symbol = "RedirectView" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "redirect:" +threat = "open_redirect" +cwe = "CWE-601" +note = "View name prefix with caller-controlled suffix" + +[[security.sinks]] +symbol = "@Query" +threat = "sql_injection" +cwe = "CWE-89" +note = "Spring Data with nativeQuery=true and concat" + +[[security.sinks]] +symbol = "createNativeQuery" +threat = "sql_injection" +cwe = "CWE-89" +note = "JPA EntityManager" + +[[security.sinks]] +symbol = "JdbcTemplate.query" +threat = "sql_injection" +cwe = "CWE-89" +note = "With concat; PreparedStatement form is safe" + +[[security.sinks]] +symbol = "SpEL" +threat = "code_injection" +cwe = "CWE-95" +note = "SpelExpressionParser.parseExpression with user input; CVE-2022-22965 class" diff --git a/knowledge/node/angular.toml b/knowledge/node/angular.toml index 4c98d17..17efdbb 100644 --- a/knowledge/node/angular.toml +++ b/knowledge/node/angular.toml @@ -23,3 +23,25 @@ role = ["framework"] function = ["templating"] layer = ["frontend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "bypassSecurityTrustHtml" +threat = "xss" +cwe = "CWE-79" +note = "DomSanitizer" + +[[security.sinks]] +symbol = "bypassSecurityTrustUrl" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "bypassSecurityTrustScript" +threat = "xss" +cwe = "CWE-79" + +[[security.sinks]] +symbol = "innerHTML" +threat = "xss" +cwe = "CWE-79" +note = "[innerHTML] binding; sanitizes by default but bypasses exist" diff --git a/knowledge/node/drizzle.toml b/knowledge/node/drizzle.toml index 0e8065b..1084fd6 100644 --- a/knowledge/node/drizzle.toml +++ b/knowledge/node/drizzle.toml @@ -23,3 +23,9 @@ files = ["drizzle.config.ts", "drizzle.config.js"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "sql.raw" +threat = "sql_injection" +cwe = "CWE-89" +note = "sql template tag is safe; sql.raw bypasses" diff --git a/knowledge/node/express.toml b/knowledge/node/express.toml index 942a3d6..cb032a0 100644 --- a/knowledge/node/express.toml +++ b/knowledge/node/express.toml @@ -15,3 +15,35 @@ role = ["framework"] function = ["api-development"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "res.send" +threat = "xss" +cwe = "CWE-79" +note = "When sending raw HTML without Content-Type override" + +[[security.sinks]] +symbol = "res.write" +threat = "xss" +cwe = "CWE-79" + +[[security.sinks]] +symbol = "res.redirect" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "res.sendFile" +threat = "path_traversal" +cwe = "CWE-22" + +[[security.sinks]] +symbol = "res.download" +threat = "path_traversal" +cwe = "CWE-22" + +[[security.sinks]] +symbol = "res.render" +threat = "ssti" +cwe = "CWE-1336" +note = "When view name is caller-controlled" diff --git a/knowledge/node/fastify.toml b/knowledge/node/fastify.toml index 6cdb11b..9ff91eb 100644 --- a/knowledge/node/fastify.toml +++ b/knowledge/node/fastify.toml @@ -15,3 +15,20 @@ role = ["framework"] function = ["api-development"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "reply.send" +threat = "xss" +cwe = "CWE-79" +note = "When sending HTML with type set to text/html" + +[[security.sinks]] +symbol = "reply.redirect" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "reply.sendFile" +threat = "path_traversal" +cwe = "CWE-22" +note = "Via @fastify/static" diff --git a/knowledge/node/hono.toml b/knowledge/node/hono.toml index c5dd002..79bfc71 100644 --- a/knowledge/node/hono.toml +++ b/knowledge/node/hono.toml @@ -15,3 +15,14 @@ role = ["framework"] function = ["api-development"] layer = ["backend", "edge"] domain = ["web-development"] + +[[security.sinks]] +symbol = "c.html" +threat = "xss" +cwe = "CWE-79" +note = "Renders without escaping; use jsx or escape manually" + +[[security.sinks]] +symbol = "c.redirect" +threat = "open_redirect" +cwe = "CWE-601" diff --git a/knowledge/node/knex.toml b/knowledge/node/knex.toml index 1f05e85..d53301c 100644 --- a/knowledge/node/knex.toml +++ b/knowledge/node/knex.toml @@ -21,3 +21,34 @@ files = ["knexfile.js", "knexfile.ts"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "raw" +threat = "sql_injection" +cwe = "CWE-89" +note = "knex.raw with interpolation; bindings are safe" + +[[security.sinks]] +symbol = "whereRaw" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "joinRaw" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "havingRaw" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "orderByRaw" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "groupByRaw" +threat = "sql_injection" +cwe = "CWE-89" diff --git a/knowledge/node/koa.toml b/knowledge/node/koa.toml index 9571c3f..ab24419 100644 --- a/knowledge/node/koa.toml +++ b/knowledge/node/koa.toml @@ -15,3 +15,14 @@ role = ["framework"] function = ["api-development"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "ctx.body" +threat = "xss" +cwe = "CWE-79" +note = "When set to raw HTML string" + +[[security.sinks]] +symbol = "ctx.redirect" +threat = "open_redirect" +cwe = "CWE-601" diff --git a/knowledge/node/mikro-orm.toml b/knowledge/node/mikro-orm.toml index 50ab61c..7007716 100644 --- a/knowledge/node/mikro-orm.toml +++ b/knowledge/node/mikro-orm.toml @@ -14,3 +14,15 @@ ecosystems = ["node"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "execute" +threat = "sql_injection" +cwe = "CWE-89" +note = "em.execute / em.getConnection().execute" + +[[security.sinks]] +symbol = "raw" +threat = "sql_injection" +cwe = "CWE-89" +note = "qb.raw" diff --git a/knowledge/node/mongoose.toml b/knowledge/node/mongoose.toml index 4f4e1d2..905bd81 100644 --- a/knowledge/node/mongoose.toml +++ b/knowledge/node/mongoose.toml @@ -14,3 +14,26 @@ ecosystems = ["node"] role = ["library"] function = ["data-mapping", "validation"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "$where" +threat = "nosql_injection" +cwe = "CWE-943" +note = "Executes JavaScript on the MongoDB server" + +[[security.sinks]] +symbol = "mapReduce" +threat = "nosql_injection" +cwe = "CWE-943" + +[[security.sinks]] +symbol = "find" +threat = "nosql_injection" +cwe = "CWE-943" +note = "When query object built from req.body without sanitization; operator injection" + +[[security.sinks]] +symbol = "aggregate" +threat = "nosql_injection" +cwe = "CWE-943" +note = "With $function or $accumulator stages" diff --git a/knowledge/node/nestjs.toml b/knowledge/node/nestjs.toml index fc2b5ed..d54b577 100644 --- a/knowledge/node/nestjs.toml +++ b/knowledge/node/nestjs.toml @@ -23,3 +23,15 @@ role = ["framework"] function = ["api-development", "dependency-injection"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "@Redirect" +threat = "open_redirect" +cwe = "CWE-601" +note = "When url is caller-controlled" + +[[security.sinks]] +symbol = "res.send" +threat = "xss" +cwe = "CWE-79" +note = "Express passthrough" diff --git a/knowledge/node/nextjs.toml b/knowledge/node/nextjs.toml index 5a244b1..77cd3b9 100644 --- a/knowledge/node/nextjs.toml +++ b/knowledge/node/nextjs.toml @@ -26,3 +26,25 @@ role = ["framework"] function = ["api-development", "templating"] layer = ["full-stack", "frontend", "backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "dangerouslySetInnerHTML" +threat = "xss" +cwe = "CWE-79" + +[[security.sinks]] +symbol = "redirect" +threat = "open_redirect" +cwe = "CWE-601" +note = "next/navigation redirect in route handlers" + +[[security.sinks]] +symbol = "NextResponse.redirect" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "rewrites" +threat = "ssrf" +cwe = "CWE-918" +note = "next.config.js rewrites with caller-controlled destination" diff --git a/knowledge/node/nuxt.toml b/knowledge/node/nuxt.toml index 4f12627..39b6a04 100644 --- a/knowledge/node/nuxt.toml +++ b/knowledge/node/nuxt.toml @@ -26,3 +26,21 @@ role = ["framework"] function = ["api-development", "templating"] layer = ["full-stack", "frontend", "backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "v-html" +threat = "xss" +cwe = "CWE-79" +note = "Vue directive that bypasses escaping" + +[[security.sinks]] +symbol = "navigateTo" +threat = "open_redirect" +cwe = "CWE-601" +note = "With external: true" + +[[security.sinks]] +symbol = "sendRedirect" +threat = "open_redirect" +cwe = "CWE-601" +note = "h3 server utility" diff --git a/knowledge/node/prisma.toml b/knowledge/node/prisma.toml index c2bb4c5..2aed096 100644 --- a/knowledge/node/prisma.toml +++ b/knowledge/node/prisma.toml @@ -23,3 +23,24 @@ files = ["prisma/schema.prisma"] role = ["library"] function = ["data-mapping", "code-generation"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "$queryRaw" +threat = "sql_injection" +cwe = "CWE-89" +note = "Tagged template is safe; concat is not" + +[[security.sinks]] +symbol = "$queryRawUnsafe" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "$executeRaw" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "$executeRawUnsafe" +threat = "sql_injection" +cwe = "CWE-89" diff --git a/knowledge/node/react.toml b/knowledge/node/react.toml index 14fcd9d..9dd2e2f 100644 --- a/knowledge/node/react.toml +++ b/knowledge/node/react.toml @@ -18,3 +18,14 @@ role = ["library"] function = ["templating"] layer = ["frontend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "dangerouslySetInnerHTML" +threat = "xss" +cwe = "CWE-79" + +[[security.sinks]] +symbol = "href" +threat = "xss" +cwe = "CWE-79" +note = "javascript: URLs in ; React 19+ blocks by default" diff --git a/knowledge/node/remix.toml b/knowledge/node/remix.toml index f361473..f639afa 100644 --- a/knowledge/node/remix.toml +++ b/knowledge/node/remix.toml @@ -23,3 +23,14 @@ role = ["framework"] function = ["api-development", "templating"] layer = ["full-stack", "frontend", "backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "dangerouslySetInnerHTML" +threat = "xss" +cwe = "CWE-79" + +[[security.sinks]] +symbol = "redirect" +threat = "open_redirect" +cwe = "CWE-601" +note = "@remix-run/node redirect" diff --git a/knowledge/node/sequelize.toml b/knowledge/node/sequelize.toml index f6a65a0..fcc8ea9 100644 --- a/knowledge/node/sequelize.toml +++ b/knowledge/node/sequelize.toml @@ -21,3 +21,27 @@ files = [".sequelizerc"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "query" +threat = "sql_injection" +cwe = "CWE-89" +note = "sequelize.query with interpolated string" + +[[security.sinks]] +symbol = "literal" +threat = "sql_injection" +cwe = "CWE-89" +note = "Sequelize.literal" + +[[security.sinks]] +symbol = "fn" +threat = "sql_injection" +cwe = "CWE-89" +note = "When fn args are caller-controlled" + +[[security.sinks]] +symbol = "where" +threat = "sql_injection" +cwe = "CWE-89" +note = "With literal fragment" diff --git a/knowledge/node/svelte.toml b/knowledge/node/svelte.toml index 63dbe31..20d2d60 100644 --- a/knowledge/node/svelte.toml +++ b/knowledge/node/svelte.toml @@ -22,3 +22,8 @@ role = ["framework", "compiler"] function = ["templating"] layer = ["frontend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "{@html}" +threat = "xss" +cwe = "CWE-79" diff --git a/knowledge/node/sveltekit.toml b/knowledge/node/sveltekit.toml index 905942b..c23586d 100644 --- a/knowledge/node/sveltekit.toml +++ b/knowledge/node/sveltekit.toml @@ -26,3 +26,15 @@ role = ["framework"] function = ["api-development", "templating"] layer = ["full-stack", "frontend", "backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "{@html}" +threat = "xss" +cwe = "CWE-79" +note = "Svelte directive that bypasses escaping" + +[[security.sinks]] +symbol = "redirect" +threat = "open_redirect" +cwe = "CWE-601" +note = "@sveltejs/kit redirect" diff --git a/knowledge/node/typeorm.toml b/knowledge/node/typeorm.toml index 5217c75..9c8c123 100644 --- a/knowledge/node/typeorm.toml +++ b/knowledge/node/typeorm.toml @@ -21,3 +21,15 @@ files = ["ormconfig.json", "ormconfig.yml", "ormconfig.yaml", "ormconfig.js", "o role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "query" +threat = "sql_injection" +cwe = "CWE-89" +note = "EntityManager.query / Repository.query" + +[[security.sinks]] +symbol = "createQueryBuilder" +threat = "sql_injection" +cwe = "CWE-89" +note = "When .where/.andWhere take raw fragments" diff --git a/knowledge/node/vue.toml b/knowledge/node/vue.toml index b98e4f1..f5bf8e9 100644 --- a/knowledge/node/vue.toml +++ b/knowledge/node/vue.toml @@ -18,3 +18,14 @@ role = ["framework"] function = ["templating"] layer = ["frontend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "v-html" +threat = "xss" +cwe = "CWE-79" + +[[security.sinks]] +symbol = "innerHTML" +threat = "xss" +cwe = "CWE-79" +note = "Direct DOM manipulation in lifecycle hooks" diff --git a/knowledge/php/laravel.toml b/knowledge/php/laravel.toml index 0e12612..e26e370 100644 --- a/knowledge/php/laravel.toml +++ b/knowledge/php/laravel.toml @@ -19,3 +19,90 @@ role = ["framework"] function = ["api-development", "templating", "data-mapping", "authentication"] layer = ["backend", "full-stack"] domain = ["web-development"] + +[[security.sinks]] +symbol = "{!! !!}" +threat = "xss" +cwe = "CWE-79" +note = "Blade unescaped output syntax" + +[[security.sinks]] +symbol = "Blade::render" +threat = "ssti" +cwe = "CWE-1336" +note = "When template body is caller-controlled" + +[[security.sinks]] +symbol = "DB::raw" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "DB::statement" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "DB::select" +threat = "sql_injection" +cwe = "CWE-89" +note = "With interpolation; bindings are safe" + +[[security.sinks]] +symbol = "whereRaw" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "selectRaw" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "orderByRaw" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "havingRaw" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "groupByRaw" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "redirect()->to" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "redirect()->away" +threat = "open_redirect" +cwe = "CWE-601" +note = "Explicitly allows external URLs" + +[[security.sinks]] +symbol = "Storage::get" +threat = "path_traversal" +cwe = "CWE-22" +note = "When path is from request" + +[[security.sinks]] +symbol = "Storage::download" +threat = "path_traversal" +cwe = "CWE-22" + +[[security.sinks]] +symbol = "fill" +threat = "mass_assignment" +cwe = "CWE-915" +note = "Without $fillable or $guarded" + +[[security.sinks]] +symbol = "forceFill" +threat = "mass_assignment" +cwe = "CWE-915" +note = "Bypasses $fillable" diff --git a/knowledge/php/symfony.toml b/knowledge/php/symfony.toml index 3a7c8cd..60a9e8a 100644 --- a/knowledge/php/symfony.toml +++ b/knowledge/php/symfony.toml @@ -19,3 +19,31 @@ role = ["framework"] function = ["api-development", "templating", "dependency-injection"] layer = ["backend", "full-stack"] domain = ["web-development"] + +[[security.sinks]] +symbol = "|raw" +threat = "xss" +cwe = "CWE-79" +note = "Twig filter that bypasses escaping" + +[[security.sinks]] +symbol = "createNativeQuery" +threat = "sql_injection" +cwe = "CWE-89" +note = "Doctrine raw SQL" + +[[security.sinks]] +symbol = "RedirectResponse" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "redirect" +threat = "open_redirect" +cwe = "CWE-601" +note = "AbstractController::redirect" + +[[security.sinks]] +symbol = "BinaryFileResponse" +threat = "path_traversal" +cwe = "CWE-22" diff --git a/knowledge/python/django.toml b/knowledge/python/django.toml index ecdc557..fe0c763 100644 --- a/knowledge/python/django.toml +++ b/knowledge/python/django.toml @@ -22,3 +22,73 @@ role = ["framework"] function = ["api-development", "templating", "data-mapping", "authentication"] layer = ["backend", "full-stack"] domain = ["web-development"] + +[[security.sinks]] +symbol = "mark_safe" +threat = "xss" +cwe = "CWE-79" +note = "Marks string as safe, bypassing template auto-escaping" + +[[security.sinks]] +symbol = "SafeString" +threat = "xss" +cwe = "CWE-79" + +[[security.sinks]] +symbol = "|safe" +threat = "xss" +cwe = "CWE-79" +note = "Template filter that bypasses escaping" + +[[security.sinks]] +symbol = "format_html" +threat = "xss" +cwe = "CWE-79" +note = "When format args are not from format_html_join" + +[[security.sinks]] +symbol = "extra" +threat = "sql_injection" +cwe = "CWE-89" +note = "QuerySet.extra() with select/where/tables strings" + +[[security.sinks]] +symbol = "RawSQL" +threat = "sql_injection" +cwe = "CWE-89" +note = "django.db.models.expressions.RawSQL" + +[[security.sinks]] +symbol = "raw" +threat = "sql_injection" +cwe = "CWE-89" +note = "Manager.raw() with interpolated query" + +[[security.sinks]] +symbol = "cursor.execute" +threat = "sql_injection" +cwe = "CWE-89" +note = "connection.cursor() with format string" + +[[security.sinks]] +symbol = "redirect" +threat = "open_redirect" +cwe = "CWE-601" +note = "When target is from request data" + +[[security.sinks]] +symbol = "HttpResponseRedirect" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "FileResponse" +threat = "path_traversal" +cwe = "CWE-22" +note = "When path is built from request data" + +[[security.sinks]] +symbol = "csrf_exempt" +threat = "csrf" +cwe = "CWE-352" +note = "Decorator that disables CSRF protection" diff --git a/knowledge/python/fastapi.toml b/knowledge/python/fastapi.toml index 755fd85..3b95b87 100644 --- a/knowledge/python/fastapi.toml +++ b/knowledge/python/fastapi.toml @@ -18,3 +18,19 @@ role = ["framework"] function = ["api-development", "validation"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "HTMLResponse" +threat = "xss" +cwe = "CWE-79" +note = "When content is not pre-escaped" + +[[security.sinks]] +symbol = "RedirectResponse" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "FileResponse" +threat = "path_traversal" +cwe = "CWE-22" diff --git a/knowledge/python/flask.toml b/knowledge/python/flask.toml index 6a476e5..0380a42 100644 --- a/knowledge/python/flask.toml +++ b/knowledge/python/flask.toml @@ -18,3 +18,37 @@ role = ["framework"] function = ["api-development", "templating"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "Markup" +threat = "xss" +cwe = "CWE-79" +note = "jinja2.Markup or markupsafe.Markup; bypasses escaping" + +[[security.sinks]] +symbol = "|safe" +threat = "xss" +cwe = "CWE-79" +note = "Jinja2 filter" + +[[security.sinks]] +symbol = "render_template_string" +threat = "ssti" +cwe = "CWE-1336" +note = "When template body is caller-controlled" + +[[security.sinks]] +symbol = "redirect" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "send_file" +threat = "path_traversal" +cwe = "CWE-22" + +[[security.sinks]] +symbol = "send_from_directory" +threat = "path_traversal" +cwe = "CWE-22" +note = "Safer than send_file but check directory arg" diff --git a/knowledge/python/peewee.toml b/knowledge/python/peewee.toml index 6c776c5..69af6cf 100644 --- a/knowledge/python/peewee.toml +++ b/knowledge/python/peewee.toml @@ -14,3 +14,15 @@ ecosystems = ["python"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "SQL" +threat = "sql_injection" +cwe = "CWE-89" +note = "Raw SQL fragment" + +[[security.sinks]] +symbol = "execute_sql" +threat = "sql_injection" +cwe = "CWE-89" +note = "Database.execute_sql" diff --git a/knowledge/python/sqlalchemy.toml b/knowledge/python/sqlalchemy.toml index 856aaf7..83f52cd 100644 --- a/knowledge/python/sqlalchemy.toml +++ b/knowledge/python/sqlalchemy.toml @@ -14,3 +14,26 @@ ecosystems = ["python"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "text" +threat = "sql_injection" +cwe = "CWE-89" +note = "sqlalchemy.text() marks raw SQL; bound params are safe" + +[[security.sinks]] +symbol = "execute" +threat = "sql_injection" +cwe = "CWE-89" +note = "Connection.execute with f-string" + +[[security.sinks]] +symbol = "exec_driver_sql" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "engine.execute" +threat = "sql_injection" +cwe = "CWE-89" +note = "Deprecated in 2.0" diff --git a/knowledge/python/tortoise-orm.toml b/knowledge/python/tortoise-orm.toml index a3eca6b..4769144 100644 --- a/knowledge/python/tortoise-orm.toml +++ b/knowledge/python/tortoise-orm.toml @@ -14,3 +14,14 @@ ecosystems = ["python"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "execute_query" +threat = "sql_injection" +cwe = "CWE-89" +note = "connections.execute_query" + +[[security.sinks]] +symbol = "RawSQL" +threat = "sql_injection" +cwe = "CWE-89" diff --git a/knowledge/ruby/activerecord.toml b/knowledge/ruby/activerecord.toml index 6d22494..740ac1a 100644 --- a/knowledge/ruby/activerecord.toml +++ b/knowledge/ruby/activerecord.toml @@ -22,3 +22,121 @@ files = ["db/migrate/", "db/schema.rb", "config/database.yml"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "find_by_sql" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "where" +threat = "sql_injection" +cwe = "CWE-89" +note = "With string interpolation; safe with hash or parameterized array" + +[[security.sinks]] +symbol = "select" +threat = "sql_injection" +cwe = "CWE-89" +note = "With string argument from user input" + +[[security.sinks]] +symbol = "order" +threat = "sql_injection" +cwe = "CWE-89" +note = "Column name not parameterizable; allowlist instead" + +[[security.sinks]] +symbol = "reorder" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "group" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "having" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "joins" +threat = "sql_injection" +cwe = "CWE-89" +note = "With raw string; symbol/hash forms are safe" + +[[security.sinks]] +symbol = "from" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "lock" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "pluck" +threat = "sql_injection" +cwe = "CWE-89" +note = "With Arel.sql wrapped string" + +[[security.sinks]] +symbol = "calculate" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "exists?" +threat = "sql_injection" +cwe = "CWE-89" +note = "With string argument" + +[[security.sinks]] +symbol = "delete_all" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "update_all" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "connection.execute" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "connection.exec_query" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "Arel.sql" +threat = "sql_injection" +cwe = "CWE-89" +note = "Marks string as safe SQL, bypassing protections" + +[[security.sinks]] +symbol = "new" +threat = "mass_assignment" +cwe = "CWE-915" +note = "When passed unfiltered params hash" + +[[security.sinks]] +symbol = "create" +threat = "mass_assignment" +cwe = "CWE-915" + +[[security.sinks]] +symbol = "update" +threat = "mass_assignment" +cwe = "CWE-915" + +[[security.sinks]] +symbol = "assign_attributes" +threat = "mass_assignment" +cwe = "CWE-915" diff --git a/knowledge/ruby/rails.toml b/knowledge/ruby/rails.toml index 4558f97..f9b29bc 100644 --- a/knowledge/ruby/rails.toml +++ b/knowledge/ruby/rails.toml @@ -23,3 +23,105 @@ role = ["framework"] function = ["api-development", "templating", "authentication"] layer = ["backend", "full-stack"] domain = ["web-development"] + +[[security.sinks]] +symbol = "html_safe" +threat = "xss" +cwe = "CWE-79" +note = "Marks string as safe, bypassing ActionView output escaping" + +[[security.sinks]] +symbol = "raw" +threat = "xss" +cwe = "CWE-79" +note = "ActionView helper, equivalent to html_safe" + +[[security.sinks]] +symbol = "safe_join" +threat = "xss" +cwe = "CWE-79" +note = "Joins array with html_safe; safe only if elements are pre-escaped" + +[[security.sinks]] +symbol = "sanitize" +threat = "xss" +cwe = "CWE-79" +note = "With custom :tags or :attributes options; default config is safe" + +[[security.sinks]] +symbol = "render inline:" +threat = "ssti" +cwe = "CWE-1336" +note = "Renders string as ERB template; CVE-2016-0752" + +[[security.sinks]] +symbol = "render text:" +threat = "xss" +cwe = "CWE-79" +note = "Renders without HTML escaping; removed in Rails 5" + +[[security.sinks]] +symbol = "render file:" +threat = "path_traversal" +cwe = "CWE-22" +note = "Renders arbitrary file as template" + +[[security.sinks]] +symbol = "redirect_to" +threat = "open_redirect" +cwe = "CWE-601" +note = "When target is from params; use redirect_to with allow_other_host: false" + +[[security.sinks]] +symbol = "redirect_back" +threat = "open_redirect" +cwe = "CWE-601" +note = "Trusts Referer header" + +[[security.sinks]] +symbol = "send_file" +threat = "path_traversal" +cwe = "CWE-22" +note = "When path is built from params" + +[[security.sinks]] +symbol = "send_data" +threat = "path_traversal" +cwe = "CWE-22" +note = "Less risky than send_file but check filename param" + +[[security.sinks]] +symbol = "constantize" +threat = "unsafe_reflection" +cwe = "CWE-470" +note = "Resolves arbitrary string to a class" + +[[security.sinks]] +symbol = "safe_constantize" +threat = "unsafe_reflection" +cwe = "CWE-470" +note = "Returns nil instead of raising but still resolves any constant" + +[[security.sinks]] +symbol = "permit!" +threat = "mass_assignment" +cwe = "CWE-915" +note = "Permits all params, bypasses strong parameters" + +[[security.sinks]] +symbol = "link_to" +threat = "xss" +cwe = "CWE-79" +note = "When href is from params; javascript: scheme" + +[[security.sinks]] +symbol = "url_for" +threat = "open_redirect" +cwe = "CWE-601" +note = "When :host option is caller-controlled" + +[[security.sinks]] +symbol = "Marshal.load" +threat = "deserialization" +cwe = "CWE-502" +note = "Used by Rails session store and cache; CVE-2013-0156 class" diff --git a/knowledge/ruby/sequel.toml b/knowledge/ruby/sequel.toml index acb5b21..4ec9507 100644 --- a/knowledge/ruby/sequel.toml +++ b/knowledge/ruby/sequel.toml @@ -14,3 +14,37 @@ ecosystems = ["ruby"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "Sequel.lit" +threat = "sql_injection" +cwe = "CWE-89" +note = "Marks string as literal SQL" + +[[security.sinks]] +symbol = "[]" +threat = "sql_injection" +cwe = "CWE-89" +note = "DB[string] runs raw SQL" + +[[security.sinks]] +symbol = "run" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "execute" +threat = "sql_injection" +cwe = "CWE-89" + +[[security.sinks]] +symbol = "fetch" +threat = "sql_injection" +cwe = "CWE-89" +note = "DB.fetch with interpolated string; placeholders are safe" + +[[security.sinks]] +symbol = "where" +threat = "sql_injection" +cwe = "CWE-89" +note = "With Sequel.lit fragment" diff --git a/knowledge/ruby/sinatra.toml b/knowledge/ruby/sinatra.toml index 8987777..3625d6f 100644 --- a/knowledge/ruby/sinatra.toml +++ b/knowledge/ruby/sinatra.toml @@ -18,3 +18,29 @@ role = ["framework"] function = ["api-development", "templating"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "erb" +threat = "ssti" +cwe = "CWE-1336" +note = "When template name or body is caller-controlled" + +[[security.sinks]] +symbol = "haml" +threat = "ssti" +cwe = "CWE-1336" + +[[security.sinks]] +symbol = "slim" +threat = "ssti" +cwe = "CWE-1336" + +[[security.sinks]] +symbol = "redirect" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "send_file" +threat = "path_traversal" +cwe = "CWE-22" diff --git a/knowledge/rust/actix.toml b/knowledge/rust/actix.toml index 027c558..2219bc7 100644 --- a/knowledge/rust/actix.toml +++ b/knowledge/rust/actix.toml @@ -15,3 +15,20 @@ role = ["framework"] function = ["api-development"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "HttpResponse::Ok().body" +threat = "xss" +cwe = "CWE-79" +note = "When body is HTML and Content-Type is text/html" + +[[security.sinks]] +symbol = "Redirect::to" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "NamedFile::open" +threat = "path_traversal" +cwe = "CWE-22" +note = "actix-files" diff --git a/knowledge/rust/axum.toml b/knowledge/rust/axum.toml index 4e150a0..98bbf74 100644 --- a/knowledge/rust/axum.toml +++ b/knowledge/rust/axum.toml @@ -18,3 +18,19 @@ role = ["framework"] function = ["api-development"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "Html" +threat = "xss" +cwe = "CWE-79" +note = "axum::response::Html does not escape" + +[[security.sinks]] +symbol = "Redirect::to" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "Redirect::permanent" +threat = "open_redirect" +cwe = "CWE-601" diff --git a/knowledge/rust/diesel.toml b/knowledge/rust/diesel.toml index 5578fda..fdc5d47 100644 --- a/knowledge/rust/diesel.toml +++ b/knowledge/rust/diesel.toml @@ -22,3 +22,15 @@ files = ["diesel.toml"] role = ["library"] function = ["data-mapping"] layer = ["data-layer"] + +[[security.sinks]] +symbol = "sql_query" +threat = "sql_injection" +cwe = "CWE-89" +note = "diesel::sql_query with format!" + +[[security.sinks]] +symbol = "sql" +threat = "sql_injection" +cwe = "CWE-89" +note = "diesel::dsl::sql in expressions" diff --git a/knowledge/rust/rocket.toml b/knowledge/rust/rocket.toml index e582e6a..df9e48e 100644 --- a/knowledge/rust/rocket.toml +++ b/knowledge/rust/rocket.toml @@ -18,3 +18,19 @@ role = ["framework"] function = ["api-development", "templating"] layer = ["backend"] domain = ["web-development"] + +[[security.sinks]] +symbol = "RawHtml" +threat = "xss" +cwe = "CWE-79" +note = "rocket::response::content::RawHtml" + +[[security.sinks]] +symbol = "Redirect::to" +threat = "open_redirect" +cwe = "CWE-601" + +[[security.sinks]] +symbol = "NamedFile::open" +threat = "path_traversal" +cwe = "CWE-22"