@runsec/mcp 1.0.35 → 1.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data/.rag-cache.json +1 -0
- package/dist/data/skills/_exploit_overrides.json +16 -0
- package/dist/data/skills/advanced-agent-cloud/index.md +94 -0
- package/dist/data/skills/advanced-agent-cloud/patterns.md +46 -0
- package/dist/data/skills/advanced-agent-cloud/skill.json +38 -0
- package/dist/data/skills/app-logic/index.md +69 -0
- package/dist/data/skills/app-logic/patterns.md +23 -0
- package/dist/data/skills/app-logic/skill.json +24 -0
- package/dist/data/skills/auth-keycloak/index.md +69 -0
- package/dist/data/skills/auth-keycloak/patterns.md +46 -0
- package/dist/data/skills/auth-keycloak/skill.json +51 -0
- package/dist/data/skills/browser-agent/index.md +58 -0
- package/dist/data/skills/browser-agent/patterns.md +15 -0
- package/dist/data/skills/browser-agent/skill.json +24 -0
- package/dist/data/skills/cloud-secrets/index.md +66 -0
- package/dist/data/skills/cloud-secrets/patterns.md +19 -0
- package/dist/data/skills/cloud-secrets/skill.json +28 -0
- package/dist/data/skills/csharp-dotnet/index.md +103 -0
- package/dist/data/skills/csharp-dotnet/patterns.md +270 -0
- package/dist/data/skills/csharp-dotnet/skill.json +27 -0
- package/dist/data/skills/desktop-vsto-suite/index.md +202 -0
- package/dist/data/skills/desktop-vsto-suite/patterns.md +154 -0
- package/dist/data/skills/desktop-vsto-suite/skill.json +26 -0
- package/dist/data/skills/devops-security/index.md +64 -0
- package/dist/data/skills/devops-security/patterns.md +23 -0
- package/dist/data/skills/devops-security/skill.json +42 -0
- package/dist/data/skills/domain-access-management/index.md +123 -0
- package/dist/data/skills/domain-access-management/patterns.md +58 -0
- package/dist/data/skills/domain-access-management/skill.json +36 -0
- package/dist/data/skills/domain-data-privacy/index.md +98 -0
- package/dist/data/skills/domain-data-privacy/patterns.md +48 -0
- package/dist/data/skills/domain-data-privacy/skill.json +36 -0
- package/dist/data/skills/domain-input-validation/index.md +210 -0
- package/dist/data/skills/domain-input-validation/patterns.md +158 -0
- package/dist/data/skills/domain-input-validation/skill.json +24 -0
- package/dist/data/skills/domain-platform-hardening/index.md +169 -0
- package/dist/data/skills/domain-platform-hardening/patterns.md +96 -0
- package/dist/data/skills/domain-platform-hardening/skill.json +27 -0
- package/dist/data/skills/ds-ml-security/patterns.md +137 -0
- package/dist/data/skills/fastapi-async/index.md +83 -0
- package/dist/data/skills/fastapi-async/patterns.md +329 -0
- package/dist/data/skills/fastapi-async/skill.json +32 -0
- package/dist/data/skills/frontend-react/index.md +26 -0
- package/dist/data/skills/frontend-react/patterns.md +226 -0
- package/dist/data/skills/frontend-react/skill.json +24 -0
- package/dist/data/skills/go-core/index.md +86 -0
- package/dist/data/skills/go-core/patterns.md +272 -0
- package/dist/data/skills/go-core/skill.json +22 -0
- package/dist/data/skills/hft-cpp-security/patterns.md +37 -0
- package/dist/data/skills/index.md +73 -0
- package/dist/data/skills/infra-k8s-helm/index.md +138 -0
- package/dist/data/skills/infra-k8s-helm/patterns.md +279 -0
- package/dist/data/skills/infra-k8s-helm/skill.json +41 -0
- package/dist/data/skills/integration-security/index.md +73 -0
- package/dist/data/skills/integration-security/patterns.md +132 -0
- package/dist/data/skills/integration-security/skill.json +30 -0
- package/dist/data/skills/java-enterprise/index.md +31 -0
- package/dist/data/skills/java-enterprise/patterns.md +816 -0
- package/dist/data/skills/java-enterprise/skill.json +26 -0
- package/dist/data/skills/java-spring/index.md +65 -0
- package/dist/data/skills/java-spring/patterns.md +22 -0
- package/dist/data/skills/java-spring/skill.json +23 -0
- package/dist/data/skills/license-compliance/index.md +58 -0
- package/dist/data/skills/license-compliance/patterns.md +12 -0
- package/dist/data/skills/license-compliance/skill.json +28 -0
- package/dist/data/skills/mobile-security/patterns.md +42 -0
- package/dist/data/skills/nodejs-nestjs/index.md +71 -0
- package/dist/data/skills/nodejs-nestjs/patterns.md +288 -0
- package/dist/data/skills/nodejs-nestjs/skill.json +24 -0
- package/dist/data/skills/observability/index.md +68 -0
- package/dist/data/skills/observability/patterns.md +22 -0
- package/dist/data/skills/observability/skill.json +26 -0
- package/dist/data/skills/php-security/patterns.md +202 -0
- package/dist/data/skills/ru-regulatory/index.md +72 -0
- package/dist/data/skills/ru-regulatory/patterns.md +28 -0
- package/dist/data/skills/ru-regulatory/skill.json +53 -0
- package/dist/data/skills/ruby-rails/index.md +65 -0
- package/dist/data/skills/ruby-rails/patterns.md +172 -0
- package/dist/data/skills/ruby-rails/skill.json +24 -0
- package/dist/data/skills/rust-security/patterns.md +152 -0
- package/dist/data/trufflehog-config.yaml +407 -0
- package/dist/index.js +3766 -372
- package/package.json +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_id": "java-enterprise",
|
|
3
|
+
"name": "Java Enterprise Security",
|
|
4
|
+
"activation_triggers": [
|
|
5
|
+
"java-enterprise-camunda",
|
|
6
|
+
"java-enterprise-spring-security",
|
|
7
|
+
"java-enterprise-jwt-oauth2",
|
|
8
|
+
"java-enterprise-infra"
|
|
9
|
+
],
|
|
10
|
+
"languages": ["java", "xml", "yaml", "dockerfile"],
|
|
11
|
+
"relevant_extensions": [
|
|
12
|
+
".java",
|
|
13
|
+
".xml",
|
|
14
|
+
".yaml",
|
|
15
|
+
".yml",
|
|
16
|
+
"Dockerfile"
|
|
17
|
+
],
|
|
18
|
+
"tools": [
|
|
19
|
+
"semgrep",
|
|
20
|
+
"syft",
|
|
21
|
+
"trufflehog"
|
|
22
|
+
],
|
|
23
|
+
"rules_path": "core/skills/java-enterprise/patterns.md",
|
|
24
|
+
"few_shot_examples": "core/gold-standard-testbed/enterprise_java_validation.java",
|
|
25
|
+
"security_priority": 5
|
|
26
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Java / Spring
|
|
2
|
+
|
|
3
|
+
## Stack overview
|
|
4
|
+
|
|
5
|
+
Server-side **Java** with **Spring**-style patterns: injection, deserialization, JWT, multipart, and path handling. Metrics are prefixed **`JAVA`**.
|
|
6
|
+
|
|
7
|
+
## Top threats
|
|
8
|
+
|
|
9
|
+
- Code/exec and SpEL/Jackson risks (`JAVA-001`–`JAVA-011`).
|
|
10
|
+
- XXE and Spring Security misconfig (`JAVA-012`–`JAVA-014`).
|
|
11
|
+
- Open redirect, JWT checks, crypto (`JAVA-015`–`JAVA-020`).
|
|
12
|
+
|
|
13
|
+
## Pattern catalog
|
|
14
|
+
|
|
15
|
+
Complete Anti-Pattern / Safe-Pattern definitions live in [`patterns.md`](patterns.md). The table below is a **table of contents** by metric ID.
|
|
16
|
+
|
|
17
|
+
| ID | Metric | Stack |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `JAVA-001` | Java Eval Injection: выполнение выражения из пользовательского ввода | `String expr = request.getParameter("expr");` `if (!expr.matches("^[0-9+\\-*/(). ]{1,64}$")) throw new IllegalArgumentException();` `...` `Object result = safeMathEval(expr);` |
|
|
20
|
+
| `JAVA-002` | Runtime Exec Injection: `Runtime.getRuntime().exec` со строкой команды | `String host = req.getParameter("host");` `if (!host.matches("^[a-zA-Z0-9.-]{1,255}$")) throw new IllegalArgumentException();` `...` `new ProcessBuilder("ping","-c","1",host).start();` |
|
|
21
|
+
| `JAVA-003` | ProcessBuilder Command Injection: shell-строка через `/bin/sh -c` | `String action = req.getParameter("action");` `Map<String,List<String>> allowed = Map.of("uptime", List.of("uptime"));` `...` `new ProcessBuilder(allowed.get(action)).start();` |
|
|
22
|
+
| `JAVA-004` | Unsafe Reflection: загрузка класса из пользовательского ввода | `String key = req.getParameter("handler");` `Map<String,Class<?>> allow = Map.of("health", HealthHandler.class);` `...` `Class<?> c = allow.get(key);` |
|
|
23
|
+
| `JAVA-005` | Method Invocation Injection: вызов произвольного метода через reflection | `String method = req.getParameter("method");` `Set<String> allow = Set.of("health","status");` `if (!allow.contains(method)) throw new SecurityException();` `...` `target.getClass().getMethod(method).invoke(target);` |
|
|
24
|
+
| `JAVA-006` | JDBC Command Composition: SQL/command фрагмент из input без allowlist | `String order = req.getParameter("order");` `if (!Set.of("name","created_at").contains(order)) order = "name";` `...` `String q = "SELECT * FROM users ORDER BY " + order;` |
|
|
25
|
+
| `JAVA-007` | SpEL Injection: expression parser на пользовательских данных | `String key = req.getParameter("key");` `Map<String,String> allow = Map.of("env","prod");` `...` `return allow.getOrDefault(key, "n/a");` |
|
|
26
|
+
| `JAVA-008` | Nashorn/Graal JS Injection: выполнение произвольного JS кода | `String cmd = req.getParameter("cmd");` `if (!Set.of("normalize").contains(cmd)) throw new SecurityException();` `...` `runFixedJsFunction(cmd);` |
|
|
27
|
+
| `JAVA-009` | SpEL Injection (Spring): expression из запроса исполняется в контексте | `String key = request.getParameter("key");` `Map<String,Object> allowed = Map.of("health", true);` `...` `return allowed.getOrDefault(key, false);` |
|
|
28
|
+
| `JAVA-010` | Jackson Unsafe Deserialization: default typing на недоверенных данных | `ObjectMapper mapper = new ObjectMapper();` `...` `mapper.disableDefaultTyping();` `UserDTO obj = mapper.readValue(body, UserDTO.class);` |
|
|
29
|
+
| `JAVA-011` | Log4j/JNDI Deserialization Risk: логирование сырых user-строк | `String msg = request.getParameter("msg");` `String safe = msg.replace("${", "\\${");` `...` `logger.error("msg={}", safe);` |
|
|
30
|
+
| `JAVA-012` | XXE in XML Parsers: внешние сущности не запрещены | `DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();` `f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);` `f.setFeature("http://xml.org/sax/features/external-general-entities", false);` `f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);` |
|
|
31
|
+
| `JAVA-013` | Insecure Spring Security: `permitAll()` на критичных эндпоинтах | `http.authorizeHttpRequests(auth -> auth` ` .requestMatchers("/admin/**").hasRole("ADMIN")` `);` |
|
|
32
|
+
| `JAVA-014` | CSRF Disabled Globally в stateful приложении | `http.csrf(csrf -> csrf` ` .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())` `);` |
|
|
33
|
+
| `JAVA-015` | Open Redirect in Spring MVC | `String next = request.getParameter("next");` `if (!next.startsWith("/")) next = "/";` `...` `return "redirect:" + next;` |
|
|
34
|
+
| `JAVA-016` | JWT Signature Bypass: no alg check in parser | `JwsHeader<?> h = Jwts.parserBuilder().build().parseClaimsJws(token).getHeader();` `if (!"HS256".equals(h.getAlgorithm())) throw new SecurityException();` `Claims c = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();` |
|
|
35
|
+
| `JAVA-017` | Insecure Random for tokens (`java.util.Random`) | `SecureRandom r = new SecureRandom();` `...` `byte[] token = new byte[32];` `r.nextBytes(token);` |
|
|
36
|
+
| `JAVA-018` | Hardcoded Secrets in config/code | `String jwtSecret = System.getenv("JWT_SECRET");` `if (jwtSecret == null) throw new IllegalStateException();` |
|
|
37
|
+
| `JAVA-019` | Unbounded Multipart Upload (DoS risk) | `if (file.getSize() > 5 * 1024 * 1024) throw new IllegalArgumentException();` `...` `byte[] data = file.getBytes();` |
|
|
38
|
+
| `JAVA-020` | Path Traversal in file download | `String p = request.getParameter("path");` `Path target = Paths.get(root, p).normalize();` `if (!target.startsWith(Paths.get(root))) throw new SecurityException();` |
|
|
39
|
+
|
|
40
|
+
## Verification
|
|
41
|
+
|
|
42
|
+
**Verification:** Check the gold testbed file(s) below for `Vulnerable: <ID>` markers (static Semgrep + `detection-matrix.md` ground truth).
|
|
43
|
+
|
|
44
|
+
- [`gold-standard-testbed/multi_lang_vulnerable/java_vulnerable.java`](../gold-standard-testbed/multi_lang_vulnerable/java_vulnerable.java)
|
|
45
|
+
|
|
46
|
+
After changing [`patterns.md`](patterns.md), run from the repo root:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
python scripts/sync_semgrep.py
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Workflow: Recon → Scan → Verify
|
|
53
|
+
|
|
54
|
+
### 1) Recon
|
|
55
|
+
- Map entrypoints, data flows, and trust boundaries for this stack.
|
|
56
|
+
- Identify which metrics in [`patterns.md`](patterns.md) apply to the code under review.
|
|
57
|
+
|
|
58
|
+
### 2) Scan
|
|
59
|
+
- Run Semgrep with `semgrep-rules/<skill>.yaml` (generated) and correlate with Anti-Patterns.
|
|
60
|
+
- Eliminate findings that cannot bind to a metric row.
|
|
61
|
+
|
|
62
|
+
### 3) Verify
|
|
63
|
+
- Confirm markers or scanner hits for touched IDs in the gold testbed when adding metrics.
|
|
64
|
+
- Emit findings as `Vulnerable: <PREFIX>-<NNN>` in written reviews.
|
|
65
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
| ID | Название метрики | Anti-Pattern (Vulnerable Code/YAML) | Safe-Pattern (Remediation) | Stack | Источник fix_template | Exploit scenario |
|
|
2
|
+
|---|---|---|---|---|---|---|
|
|
3
|
+
| JAVA-001 | Java Eval Injection: выполнение выражения из пользовательского ввода | `String expr = request.getParameter("expr");`<br>`...`<br>`Object result = engine.eval(expr);` | `String expr = request.getParameter("expr");`<br>`if (!expr.matches("^[0-9+\\-*/(). ]{1,64}$")) throw new IllegalArgumentException();`<br>`...`<br>`Object result = safeMathEval(expr);` | Java/Spring | `CWE-94` | `String expr = request.getParameter("expr");` `if (!expr.matches("^[0-9+\\-*/(). ]{1,64}$")) throw new IllegalArgumentException();` `...` `Object result = safeMathEval(expr);` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-001 java eval injection выполнение выражения из пользовательского ввода string expr request getparameter object result engine if matches 0 9 1 -->
|
|
4
|
+
| JAVA-002 | Runtime Exec Injection: `Runtime.getRuntime().exec` со строкой команды | `String host = req.getParameter("host");`<br>`...`<br>`Runtime.getRuntime().exec("ping -c 1 " + host);` | `String host = req.getParameter("host");`<br>`if (!host.matches("^[a-zA-Z0-9.-]{1,255}$")) throw new IllegalArgumentException();`<br>`...`<br>`new ProcessBuilder("ping","-c","1",host).start();` | Java/Spring | `CWE-78` | `String host = req.getParameter("host");` `if (!host.matches("^[a-zA-Z0-9.-]{1,255}$")) throw new IllegalArgumentException();` `...` `new ProcessBuilder("ping","-c","1",host).start();` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-002 runtime exec injection getruntime со строкой команды string host req getparameter ping c 1 if matches a za z0 9 -->
|
|
5
|
+
| JAVA-003 | ProcessBuilder Command Injection: shell-строка через `/bin/sh -c` | `String cmd = req.getParameter("cmd");`<br>`...`<br>`new ProcessBuilder("sh","-c", cmd).start();` | `String action = req.getParameter("action");`<br>`Map<String,List<String>> allowed = Map.of("uptime", List.of("uptime"));`<br>`...`<br>`new ProcessBuilder(allowed.get(action)).start();` | Java/Spring | `CWE-77` | `String action = req.getParameter("action");` `Map<String,List<String>> allowed = Map.of("uptime", List.of("uptime"));` `...` `new ProcessBuilder(allowed.get(action)).start();` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-003 processbuilder command injection shell строка через bin sh c string cmd req getparameter new start action map list allowed of -->
|
|
6
|
+
| JAVA-004 | Unsafe Reflection: загрузка класса из пользовательского ввода | `String className = req.getParameter("class");`<br>`...`<br>`Class<?> c = Class.forName(className);` | `String key = req.getParameter("handler");`<br>`Map<String,Class<?>> allow = Map.of("health", HealthHandler.class);`<br>`...`<br>`Class<?> c = allow.get(key);` | Java/Spring | `CWE-470` | `String key = req.getParameter("handler");` `Map<String,Class<?>> allow = Map.of("health", HealthHandler.class);` `...` `Class<?> c = allow.get(key);` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-004 unsafe reflection загрузка класса из пользовательского ввода string classname req getparameter class c forname key handler map allow of health -->
|
|
7
|
+
| JAVA-005 | Method Invocation Injection: вызов произвольного метода через reflection | `String method = req.getParameter("method");`<br>`...`<br>`target.getClass().getMethod(method).invoke(target);` | `String method = req.getParameter("method");`<br>`Set<String> allow = Set.of("health","status");`<br>`if (!allow.contains(method)) throw new SecurityException();`<br>`...`<br>`target.getClass().getMethod(method).invoke(target);` | Java/Spring | `CWE-74` | `String method = req.getParameter("method");` `Set<String> allow = Set.of("health","status");` `if (!allow.contains(method)) throw new SecurityException();` `...` `target.getClass().getMethod(method).invoke(target);` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-005 method invocation injection вызов произвольного метода через reflection string req getparameter target getclass getmethod invoke set allow of health status -->
|
|
8
|
+
| JAVA-006 | JDBC Command Composition: SQL/command фрагмент из input без allowlist | `String order = req.getParameter("order");`<br>`...`<br>`String q = "SELECT * FROM users ORDER BY " + order;` | `String order = req.getParameter("order");`<br>`if (!Set.of("name","created_at").contains(order)) order = "name";`<br>`...`<br>`String q = "SELECT * FROM users ORDER BY " + order;` | Java/Spring | `CWE-74` | `String order = req.getParameter("order");` `if (!Set.of("name","created_at").contains(order)) order = "name";` `...` `String q = "SELECT * FROM users ORDER BY " + order;` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-006 jdbc command composition sql фрагмент из input без allowlist string order req getparameter q select from users by if set -->
|
|
9
|
+
| JAVA-007 | SpEL Injection: expression parser на пользовательских данных | `String exp = req.getParameter("exp");`<br>`...`<br>`parser.parseExpression(exp).getValue(ctx);` | `String key = req.getParameter("key");`<br>`Map<String,String> allow = Map.of("env","prod");`<br>`...`<br>`return allow.getOrDefault(key, "n/a");` | Java/Spring | `CWE-94` | `String key = req.getParameter("key");` `Map<String,String> allow = Map.of("env","prod");` `...` `return allow.getOrDefault(key, "n/a");` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-007 spel injection expression parser на пользовательских данных string exp req getparameter parseexpression getvalue ctx key map allow of env prod -->
|
|
10
|
+
| JAVA-008 | Nashorn/Graal JS Injection: выполнение произвольного JS кода | `String js = req.getParameter("script");`<br>`...`<br>`engine.eval(js);` | `String cmd = req.getParameter("cmd");`<br>`if (!Set.of("normalize").contains(cmd)) throw new SecurityException();`<br>`...`<br>`runFixedJsFunction(cmd);` | Java/Spring | `CWE-95` | `String cmd = req.getParameter("cmd");` `if (!Set.of("normalize").contains(cmd)) throw new SecurityException();` `...` `runFixedJsFunction(cmd);` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-008 nashorn graal js injection выполнение произвольного кода string req getparameter script engine eval cmd if set of normalize contains throw -->
|
|
11
|
+
| JAVA-009 | SpEL Injection (Spring): expression из запроса исполняется в контексте | `String expr = request.getParameter("expr");`<br>`...`<br>`spelParser.parseExpression(expr).getValue(context);` | `String key = request.getParameter("key");`<br>`Map<String,Object> allowed = Map.of("health", true);`<br>`...`<br>`return allowed.getOrDefault(key, false);` | Java/Spring | `CWE-917` | `String key = request.getParameter("key");` `Map<String,Object> allowed = Map.of("health", true);` `...` `return allowed.getOrDefault(key, false);` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-009 spel injection spring expression из запроса исполняется в контексте string expr request getparameter spelparser parseexpression getvalue context key map object -->
|
|
12
|
+
| JAVA-010 | Jackson Unsafe Deserialization: default typing на недоверенных данных | `ObjectMapper mapper = new ObjectMapper();`<br>`...`<br>`mapper.enableDefaultTyping();`<br>`Object obj = mapper.readValue(body, Object.class);` | `ObjectMapper mapper = new ObjectMapper();`<br>`...`<br>`mapper.disableDefaultTyping();`<br>`UserDTO obj = mapper.readValue(body, UserDTO.class);` | Java/Spring | `CWE-502` | `ObjectMapper mapper = new ObjectMapper();` `...` `mapper.disableDefaultTyping();` `UserDTO obj = mapper.readValue(body, UserDTO.class);` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-010 jackson unsafe deserialization default typing на недоверенных данных objectmapper mapper new enabledefaulttyping object obj readvalue body class disabledefaulttyping userdto -->
|
|
13
|
+
| JAVA-011 | Log4j/JNDI Deserialization Risk: логирование сырых user-строк | `String msg = request.getParameter("msg");`<br>`...`<br>`logger.error(msg);` | `String msg = request.getParameter("msg");`<br>`String safe = msg.replace("${", "\\${");`<br>`...`<br>`logger.error("msg={}", safe);` | Java/Spring | `CWE-502` | `String msg = request.getParameter("msg");` `String safe = msg.replace("${", "\\${");` `...` `logger.error("msg={}", safe);` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-011 log4j jndi deserialization risk логирование сырых user строк string msg request getparameter logger error replace -->
|
|
14
|
+
| JAVA-012 | XXE in XML Parsers: внешние сущности не запрещены | `DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();`<br>`...`<br>`DocumentBuilder b = f.newDocumentBuilder();`<br>`Document d = b.parse(input);` | `DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();`<br>`f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);`<br>`f.setFeature("http://xml.org/sax/features/external-general-entities", false);`<br>`f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);` | Java/Spring | `CWE-611` | `DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();` `f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);` `f.setFeature("http://xml.org/sax/features/external-general-entities", false);` `f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-012 xxe xml parsers внешние сущности не запрещены documentbuilderfactory f newinstance documentbuilder b newdocumentbuilder document d parse input setfeature http apache -->
|
|
15
|
+
| JAVA-013 | Insecure Spring Security: `permitAll()` на критичных эндпоинтах | `http.authorizeHttpRequests(auth -> auth`<br>` .requestMatchers("/admin/**").permitAll()`<br>`);` | `http.authorizeHttpRequests(auth -> auth`<br>` .requestMatchers("/admin/**").hasRole("ADMIN")`<br>`);` | Java/Spring | `CWE-306` | `http.authorizeHttpRequests(auth -> auth` ` .requestMatchers("/admin/**").hasRole("ADMIN")` `);` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-013 insecure spring security permitall на критичных эндпоинтах http authorizehttprequests auth requestmatchers admin hasrole -->
|
|
16
|
+
| JAVA-014 | CSRF Disabled Globally в stateful приложении | `http.csrf(csrf -> csrf.disable());` | `http.csrf(csrf -> csrf`<br>` .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())`<br>`);` | Java/Spring | `CWE-352` | `http.csrf(csrf -> csrf` ` .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())` `);` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-014 csrf disabled globally в stateful приложении http disable csrftokenrepository cookiecsrftokenrepository withhttponlyfalse -->
|
|
17
|
+
| JAVA-015 | Open Redirect in Spring MVC | `String next = request.getParameter("next");`<br>`...`<br>`return "redirect:" + next;` | `String next = request.getParameter("next");`<br>`if (!next.startsWith("/")) next = "/";`<br>`...`<br>`return "redirect:" + next;` | Java/Spring | `CWE-601` | `String next = request.getParameter("next");` `if (!next.startsWith("/")) next = "/";` `...` `return "redirect:" + next;` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-015 open redirect spring mvc string next request getparameter return if startswith -->
|
|
18
|
+
| JAVA-016 | JWT Signature Bypass: no alg check in parser | `Claims c = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();` | `JwsHeader<?> h = Jwts.parserBuilder().build().parseClaimsJws(token).getHeader();`<br>`if (!"HS256".equals(h.getAlgorithm())) throw new SecurityException();`<br>`Claims c = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();` | Java/Spring | `CWE-347` | `JwsHeader<?> h = Jwts.parserBuilder().build().parseClaimsJws(token).getHeader();` `if (!"HS256".equals(h.getAlgorithm())) throw new SecurityException();` `Claims c = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-016 jwt signature bypass no alg check parser claims c jwts parserbuilder setsigningkey key build parseclaimsjws token getbody jwsheader h getheader -->
|
|
19
|
+
| JAVA-017 | Insecure Random for tokens (`java.util.Random`) | `Random r = new Random();`<br>`...`<br>`String token = Long.toHexString(r.nextLong());` | `SecureRandom r = new SecureRandom();`<br>`...`<br>`byte[] token = new byte[32];`<br>`r.nextBytes(token);` | Java/Spring | `CWE-338` | `SecureRandom r = new SecureRandom();` `...` `byte[] token = new byte[32];` `r.nextBytes(token);` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-017 insecure random for tokens java util r new string token long tohexstring nextlong securerandom byte 32 nextbytes -->
|
|
20
|
+
| JAVA-018 | Hardcoded Secrets in config/code | `String jwtSecret = "prod-secret-123";` | `String jwtSecret = System.getenv("JWT_SECRET");`<br>`if (jwtSecret == null) throw new IllegalStateException();` | Java/Spring | `CWE-798` | `String jwtSecret = System.getenv("JWT_SECRET");` `if (jwtSecret == null) throw new IllegalStateException();` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-018 hardcoded secrets config string jwtsecret prod secret 123 system getenv jwt if null throw new illegalstateexception -->
|
|
21
|
+
| JAVA-019 | Unbounded Multipart Upload (DoS risk) | `MultipartFile file = req.getFile("file");`<br>`...`<br>`byte[] data = file.getBytes();` | `if (file.getSize() > 5 * 1024 * 1024) throw new IllegalArgumentException();`<br>`...`<br>`byte[] data = file.getBytes();` | Java/Spring | `CWE-400` | `if (file.getSize() > 5 * 1024 * 1024) throw new IllegalArgumentException();` `...` `byte[] data = file.getBytes();` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-019 unbounded multipart upload dos risk multipartfile file req getfile byte data getbytes if getsize 5 1024 throw new illegalargumentexception -->
|
|
22
|
+
| JAVA-020 | Path Traversal in file download | `String p = request.getParameter("path");`<br>`...`<br>`Path target = Paths.get(root, p);` | `String p = request.getParameter("path");`<br>`Path target = Paths.get(root, p).normalize();`<br>`if (!target.startsWith(Paths.get(root))) throw new SecurityException();` | Java/Spring | `CWE-22` | `String p = request.getParameter("path");` `Path target = Paths.get(root, p).normalize();` `if (!target.startsWith(Paths.get(root))) throw new SecurityException();` | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: java-020 path traversal file download string p request getparameter target paths get root normalize if startswith throw new securityexception -->
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_id": "java-spring",
|
|
3
|
+
"name": "Java / Spring Security",
|
|
4
|
+
"activation_triggers": [
|
|
5
|
+
"java-spring-spel",
|
|
6
|
+
"java-jackson-typing",
|
|
7
|
+
"java-servlet-redirect",
|
|
8
|
+
"java-jwt-verifier"
|
|
9
|
+
],
|
|
10
|
+
"relevant_extensions": [
|
|
11
|
+
".java",
|
|
12
|
+
".xml",
|
|
13
|
+
".properties"
|
|
14
|
+
],
|
|
15
|
+
"tools": [
|
|
16
|
+
"semgrep",
|
|
17
|
+
"syft",
|
|
18
|
+
"trufflehog"
|
|
19
|
+
],
|
|
20
|
+
"rules_path": "core/skills/java-spring/patterns.md",
|
|
21
|
+
"few_shot_examples": "core/gold-standard-testbed/multi_lang_vulnerable/java_vulnerable.java",
|
|
22
|
+
"security_priority": 5
|
|
23
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# License Compliance
|
|
2
|
+
|
|
3
|
+
## Stack overview
|
|
4
|
+
|
|
5
|
+
Dependency manifest policy for copyleft licenses (AGPL/SSPL), trusted package sources, and SBOM/lockfile evidence for `package.json` / `requirements.txt`. Metrics are prefixed **`LIC`**.
|
|
6
|
+
|
|
7
|
+
**Verification note:** LIC checks may require running **Syft via Docker/MCP** to produce SBOM (CycloneDX/SPDX) and detect transitive license risk not visible in manifests.
|
|
8
|
+
|
|
9
|
+
## Top threats
|
|
10
|
+
|
|
11
|
+
- Direct and transitive copyleft exposure (`LIC-001`, `LIC-002`, `LIC-009`).
|
|
12
|
+
- Unknown metadata and untrusted sources in dependency pipeline (`LIC-004`, `LIC-005`).
|
|
13
|
+
- Missing CI/SBOM evidence for license governance (`LIC-006`, `LIC-008`).
|
|
14
|
+
- Binary artifact license/provenance gaps (`LIC-010`).
|
|
15
|
+
|
|
16
|
+
## Pattern catalog
|
|
17
|
+
|
|
18
|
+
Complete Anti-Pattern / Safe-Pattern definitions live in [`patterns.md`](patterns.md). The table below is a **table of contents** by metric ID.
|
|
19
|
+
|
|
20
|
+
| ID | Metric | Stack |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| `LIC-001` | AGPL-3.0 in `package.json` / `requirements.txt` | Заменить AGPL зависимость на совместимую по лицензии (MIT/BSD/Apache-2.0) и проверить транзитивные зависимости (lockfile/SBOM). |
|
|
23
|
+
| `LIC-002` | SSPL for hosted/cloud services | Исключить SSPL зависимости в облачном контуре, заменить на permissive варианты и подтвердить лицензионную совместимость (SBOM/scan). |
|
|
24
|
+
| `LIC-003` | Unmaintained / deprecated library (> 2 years) | Обновить библиотеку до поддерживаемой версии, либо заменить на альтернативу с активным мейнтейном; фиксировать версии в lockfile. |
|
|
25
|
+
| `LIC-004` | Unknown License Metadata | Разрешать только явно идентифицированные SPDX-лицензии; блокировать сборку при `UNKNOWN`/`NOASSERTION`. |
|
|
26
|
+
| `LIC-005` | Untrusted Package Source | Использовать только доверенные внутренние registry/mirror и фиксировать источник в CI policy. |
|
|
27
|
+
| `LIC-006` | Missing License Gate in CI | Добавить CI gate: `syft` + policy check (block on AGPL/GPL/SSPL according to org policy). |
|
|
28
|
+
| `LIC-008` | Missing SBOM Evidence | Генерировать SBOM через `syft` в формате CycloneDX/SPDX и сохранять как артефакт релиза. |
|
|
29
|
+
| `LIC-009` | Transitive Copyleft via Syft | Анализировать `syft`-отчет на транзитивные copyleft-лицензии и блокировать релиз до remediation/exception approval. |
|
|
30
|
+
| `LIC-010` | Binary-embedded License Risk | Для бинарных зависимостей проверять наличие license metadata/attestation и подтверждать источник/право использования. |
|
|
31
|
+
| `LIC-011` | Paladin: NuGet / внешние DLL без проверки целостности (checksum/lockfile) | Enable central package management + lockfile; verify package hashes in CI; verify binary signatures for external DLLs. |
|
|
32
|
+
|
|
33
|
+
## Verification
|
|
34
|
+
|
|
35
|
+
**Verification:** Check the gold testbed file(s) below for `Vulnerable: <ID>` markers (static Semgrep + `detection-matrix.md` ground truth).
|
|
36
|
+
|
|
37
|
+
- [`gold-standard-testbed/license_compliance_vulnerable.py`](../gold-standard-testbed/license_compliance_vulnerable.py)
|
|
38
|
+
|
|
39
|
+
After changing [`patterns.md`](patterns.md), run from the repo root:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
python scripts/sync_semgrep.py
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Workflow: Recon → Scan → Verify
|
|
46
|
+
|
|
47
|
+
### 1) Recon
|
|
48
|
+
- Map entrypoints, data flows, and trust boundaries for this stack.
|
|
49
|
+
- Identify which metrics in [`patterns.md`](patterns.md) apply to the code under review.
|
|
50
|
+
|
|
51
|
+
### 2) Scan
|
|
52
|
+
- Run Semgrep with `semgrep-rules/<skill>.yaml` (generated) and correlate with Anti-Patterns.
|
|
53
|
+
- Eliminate findings that cannot bind to a metric row.
|
|
54
|
+
|
|
55
|
+
### 3) Verify
|
|
56
|
+
- Confirm markers or scanner hits for touched IDs in the gold testbed when adding metrics.
|
|
57
|
+
- Emit findings as `Vulnerable: <PREFIX>-<NNN>` in written reviews.
|
|
58
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
| ID | Название метрики | Anti-Pattern (Vulnerable Code/YAML) | Safe-Pattern (Remediation) | Stack | Источник fix_template | Exploit scenario |
|
|
2
|
+
|---|---|---|---|---|---|---|
|
|
3
|
+
| LIC-001 | AGPL-3.0 in `package.json` / `requirements.txt` | `"license": "AGPL-3.0"`<br>`AGPL-3.0` | Заменить AGPL зависимость на совместимую по лицензии (MIT/BSD/Apache-2.0) и проверить транзитивные зависимости (lockfile/SBOM). | Compliance/License | Policy: strong copyleft (AGPL-3.0) | Заменить AGPL зависимость на совместимую по лицензии (MIT/BSD/Apache-2.0) и проверить транзитивные зависимости (lockfile/SBOM). | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: lic-001 agpl 3 0 package json requirements txt license заменить зависимость на совместимую по лицензии mit bsd apache 2 и проверить -->
|
|
4
|
+
| LIC-002 | SSPL for hosted/cloud services | `SSPL`<br>`"license": "SSPL"` | Исключить SSPL зависимости в облачном контуре, заменить на permissive варианты и подтвердить лицензионную совместимость (SBOM/scan). | Compliance/License | Policy: SSPL risk for hosted services | Исключить SSPL зависимости в облачном контуре, заменить на permissive варианты и подтвердить лицензионную совместимость (SBOM/scan). | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: lic-002 sspl for hosted cloud services license исключить зависимости в облачном контуре заменить на permissive варианты и подтвердить лицензионную совместимость sbom -->
|
|
5
|
+
| LIC-003 | Unmaintained / deprecated library (> 2 years) | `deprecated: true`<br>`last_updated: "2021-01-01"` | Обновить библиотеку до поддерживаемой версии, либо заменить на альтернативу с активным мейнтейном; фиксировать версии в lockfile. | Compliance/License | Policy: dependency maintenance SLA (2y) | Обновить библиотеку до поддерживаемой версии, либо заменить на альтернативу с активным мейнтейном; фиксировать версии в lockfile. | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: lic-003 unmaintained deprecated library 2 years true last updated 2021 01 обновить библиотеку до поддерживаемой версии либо заменить на альтернативу с -->
|
|
6
|
+
| LIC-004 | Unknown License Metadata | `"license": "UNKNOWN"`<br>`license: null` | Разрешать только явно идентифицированные SPDX-лицензии; блокировать сборку при `UNKNOWN`/`NOASSERTION`. | Compliance/License | SPDX policy baseline | Разрешать только явно идентифицированные SPDX-лицензии; блокировать сборку при `UNKNOWN`/`NOASSERTION`. | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: lic-004 unknown license metadata null разрешать только явно идентифицированные spdx лицензии блокировать сборку при noassertion -->
|
|
7
|
+
| LIC-005 | Untrusted Package Source | `pip install --index-url https://pypi.org/simple`<br>`npm config set registry https://registry.npmjs.org/` | Использовать только доверенные внутренние registry/mirror и фиксировать источник в CI policy. | Compliance/License | Supply-chain trusted source policy | Использовать только доверенные внутренние registry/mirror и фиксировать источник в CI policy. | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: lic-005 untrusted package source pip install index url https pypi org simple npm config set registry npmjs использовать только доверенные внутренние -->
|
|
8
|
+
| LIC-006 | Missing License Gate in CI | `# build runs without syft/license stage` | Добавить CI gate: `syft` + policy check (block on AGPL/GPL/SSPL according to org policy). | Compliance/License | CI/CD license governance | Добавить CI gate: `syft` + policy check (block on AGPL/GPL/SSPL according to org policy). | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: lic-006 missing license gate ci build runs without syft stage добавить policy check block on agpl gpl sspl according to org -->
|
|
9
|
+
| LIC-008 | Missing SBOM Evidence | `# no sbom artifact generated` | Генерировать SBOM через `syft` в формате CycloneDX/SPDX и сохранять как артефакт релиза. | Compliance/License | SBOM requirement (CycloneDX/SPDX) | Генерировать SBOM через `syft` в формате CycloneDX/SPDX и сохранять как артефакт релиза. | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: lic-008 missing sbom evidence no artifact generated генерировать через syft в формате cyclonedx spdx и сохранять как артефакт релиза -->
|
|
10
|
+
| LIC-009 | Transitive Copyleft via Syft | `# syft output includes GPL/AGPL transitive packages` | Анализировать `syft`-отчет на транзитивные copyleft-лицензии и блокировать релиз до remediation/exception approval. | Compliance/License | Transitive license risk management | Анализировать `syft`-отчет на транзитивные copyleft-лицензии и блокировать релиз до remediation/exception approval. | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: lic-009 transitive copyleft via syft output includes gpl agpl packages анализировать отчет на транзитивные лицензии и блокировать релиз до remediation exception -->
|
|
11
|
+
| LIC-010 | Binary-embedded License Risk | `# bundled .so/.dll without embedded license evidence` | Для бинарных зависимостей проверять наличие license metadata/attestation и подтверждать источник/право использования. | Compliance/License | Binary provenance and license policy | Для бинарных зависимостей проверять наличие license metadata/attestation и подтверждать источник/право использования. | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: lic-010 binary embedded license risk bundled so dll without evidence для бинарных зависимостей проверять наличие metadata attestation и подтверждать источник право -->
|
|
12
|
+
| LIC-011 | Paladin: NuGet / внешние DLL без проверки целостности (checksum/lockfile) | `# dotnet restore without packages.lock.json integrity gate` | Включить `RestorePackagesWithLockFile`, хранить `packages.lock.json`, в CI сверять SHA512; для native DLL — подпись/хеш перед загрузкой. | Compliance/License | `CWE-347`, `CWE-1104` | Enable central package management + lockfile; verify package hashes in CI; verify binary signatures for external DLLs. | Атакующий доставляет входные данные, соответствующие anti-pattern; реальный ущерб зависит от приёмника (sink), конфигурации и границ доверия. | <!-- semantic_anchor: lic-011 paladin nuget checksum lockfile dll integrity -->
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_id": "license-compliance",
|
|
3
|
+
"name": "License Compliance",
|
|
4
|
+
"activation_triggers": [
|
|
5
|
+
"lic-sbom-syft",
|
|
6
|
+
"lic-agpl-license",
|
|
7
|
+
"lic-gpl-family",
|
|
8
|
+
"lic-sspl-license",
|
|
9
|
+
"lic-transitive-copyleft",
|
|
10
|
+
"lic-binary-license-metadata"
|
|
11
|
+
],
|
|
12
|
+
"relevant_extensions": [
|
|
13
|
+
".json",
|
|
14
|
+
".lock",
|
|
15
|
+
".txt",
|
|
16
|
+
".toml",
|
|
17
|
+
".yaml",
|
|
18
|
+
".yml"
|
|
19
|
+
],
|
|
20
|
+
"tools": [
|
|
21
|
+
"semgrep",
|
|
22
|
+
"syft",
|
|
23
|
+
"trufflehog"
|
|
24
|
+
],
|
|
25
|
+
"rules_path": "core/skills/license-compliance/patterns.md",
|
|
26
|
+
"few_shot_examples": "core/gold-standard-testbed/license_compliance_vulnerable.py",
|
|
27
|
+
"security_priority": 5
|
|
28
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
| ID | Название метрики | Anti-Pattern (Vulnerable Code/YAML) | Safe-Pattern (Remediation) | Stack | Источник fix_template | Exploit scenario |
|
|
2
|
+
|---|---|---|---|---|---|---|
|
|
3
|
+
| MOB-001 | Android token in SharedPreferences | `prefs.edit().putString("token", token).apply()` | store tokens in Android Keystore-backed encrypted storage | Mobile (Kotlin/Swift) | CWE-922 | Use Keystore/Keychain for secrets at rest. |
|
|
4
|
+
| MOB-002 | iOS token in UserDefaults | `UserDefaults.standard.set(token, forKey:"token")` | store in Keychain with access control | Mobile (Kotlin/Swift) | CWE-922 | Persist auth artifacts only in secure enclave-backed stores. |
|
|
5
|
+
| MOB-003 | Android plaintext DB for auth secrets | `Room entity has refreshToken field` | encrypt secret fields with key in Keystore | Mobile (Kotlin/Swift) | CWE-312 | Encrypt token/PII columns before persistence. |
|
|
6
|
+
| MOB-004 | iOS plist stores API credentials | credentials in plist/resources | fetch from backend at runtime and keep ephemeral | Mobile (Kotlin/Swift) | CWE-798 | Remove hardcoded credentials from app bundle. |
|
|
7
|
+
| MOB-005 | Missing certificate pinning (OkHttp) | default trust chain only for high-risk API | add certificate pinning for critical domains | Mobile (Kotlin/Swift) | CWE-295 | Enforce pinset with rotation support. |
|
|
8
|
+
| MOB-006 | Weak custom TrustManager accepts all certs | `checkServerTrusted(...) {}` | strict TrustManager + hostname verification | Mobile (Kotlin/Swift) | CWE-295 | Reject untrusted certificates. |
|
|
9
|
+
| MOB-007 | Alamofire server trust disabled | `.disableEvaluation` in ServerTrustManager | use pinned certificates/public keys | Mobile (Kotlin/Swift) | CWE-295 | Require strong server trust evaluation. |
|
|
10
|
+
| MOB-008 | Pinning bypass on debug flag in release | `if (BuildConfig.DEBUG) skipPinning()` leaked to prod | compile-time guard and CI gate for release variants | Mobile (Kotlin/Swift) | CWE-489 | Prevent debug trust exceptions in release builds. |
|
|
11
|
+
| MOB-009 | Pinset not rotated/versioned | static pin forever | implement multiple valid pins + expiry policy | Mobile (Kotlin/Swift) | CWE-327 | Support crypto agility for pin changes. |
|
|
12
|
+
| MOB-010 | Biometric success without fallback validation | accept auth on callback without result checks | verify result codes and cryptographic challenge | Mobile (Kotlin/Swift) | CWE-287 | Bind biometric result to signed nonce. |
|
|
13
|
+
| MOB-011 | Biometric failure ignored | failure callback logs only, continues flow | enforce explicit deny and retry policy | Mobile (Kotlin/Swift) | CWE-287 | Fail closed on biometric errors. |
|
|
14
|
+
| MOB-012 | Device credential fallback forced insecurely | allow weak fallback for high-risk action | require strong auth class for sensitive operations | Mobile (Kotlin/Swift) | CWE-307 | Use strong biometric/device credential policy. |
|
|
15
|
+
| MOB-013 | LocalAuthentication result not bound to action | LAContext evaluated but token issued later without binding | sign action nonce upon biometric success | Mobile (Kotlin/Swift) | CWE-345 | Prevent replay of authentication state. |
|
|
16
|
+
| MOB-014 | Secrets kept in memory as immutable strings | `val pin = "1234"`/`String` secrets | use mutable buffers and zero after use | Mobile (Kotlin/Swift) | CWE-1037 | Minimize secret lifetime in memory. |
|
|
17
|
+
| MOB-015 | Clipboard used for secrets | copy token/password to clipboard | avoid clipboard or auto-clear with warnings | Mobile (Kotlin/Swift) | CWE-200 | Prevent accidental secret exfiltration. |
|
|
18
|
+
| MOB-016 | WebView JavaScript interface exposed broadly | addJavascriptInterface without origin controls | restrict interface and validate origins/messages | Mobile (Kotlin/Swift) | CWE-749 | Limit WebView bridge attack surface. |
|
|
19
|
+
| MOB-017 | WebView loads mixed content | allow HTTP subresources | enforce HTTPS-only and secure content mode | Mobile (Kotlin/Swift) | CWE-319 | Block mixed content downgrade. |
|
|
20
|
+
| MOB-018 | Android debuggable enabled in release | `android:debuggable="true"` | disable debuggable in release manifest/build | Mobile (Kotlin/Swift) | CWE-489 | Prevent runtime introspection in production. |
|
|
21
|
+
| MOB-019 | iOS jailbreak detection absent for high-risk operations | no jailbreak checks before payment action | add jailbreak/root detection with policy gating | Mobile (Kotlin/Swift) | CWE-693 | Add runtime integrity checks for sensitive flows. |
|
|
22
|
+
| MOB-020 | Android root detection absent for high-risk operations | no root checks before transfer | enforce root detection and degraded mode | Mobile (Kotlin/Swift) | CWE-693 | Restrict critical actions on rooted devices. |
|
|
23
|
+
| MOB-021 | isDebuggerPresent absent for anti-tamper path | no debugger checks in sensitive workflow | detect debugger and enforce hardened response | Mobile (Kotlin/Swift) | CWE-489 | Add anti-debug checks for critical paths. |
|
|
24
|
+
| MOB-022 | Hardcoded API endpoints allow easy MITM reroute | base URL in mutable plain config | sign/verify endpoint config and pin certificates | Mobile (Kotlin/Swift) | CWE-346 | Protect endpoint integrity. |
|
|
25
|
+
| MOB-023 | Insecure deep link handling | accepts arbitrary URI host/path | strict deep-link allowlist and auth checks | Mobile (Kotlin/Swift) | CWE-939 | Validate deep links before navigation/actions. |
|
|
26
|
+
| MOB-024 | Exported Android components without permissions | exported activity/service handles sensitive intents | set exported=false or require signature permission | Mobile (Kotlin/Swift) | CWE-926 | Restrict component exposure. |
|
|
27
|
+
| MOB-025 | iOS URL scheme handler missing source-app validation | trust any URL invocation | validate source app and request signature | Mobile (Kotlin/Swift) | CWE-346 | Authenticate inter-app communication. |
|
|
28
|
+
| MOB-026 | Screenshots allowed on sensitive screens | no secure flag for secrets | set FLAG_SECURE / secure text entry equivalents | Mobile (Kotlin/Swift) | CWE-200 | Block shoulder-surfing/screenshot leaks. |
|
|
29
|
+
| MOB-027 | Logs include PII/secrets in release builds | `Log.d("auth", token)` | structured redacted logging with release restrictions | Mobile (Kotlin/Swift) | CWE-532 | Avoid secret disclosure via logs. |
|
|
30
|
+
| MOB-028 | Push notification payload includes secret data | full token/account data in notification | send opaque IDs only, fetch securely in-app | Mobile (Kotlin/Swift) | CWE-359 | Keep sensitive data off push channels. |
|
|
31
|
+
| MOB-029 | Weak random generation for session/nonce | `Random()` for auth nonce | use SecureRandom / SecRandomCopyBytes | Mobile (Kotlin/Swift) | CWE-330 | Generate cryptographically strong nonces. |
|
|
32
|
+
| MOB-030 | Missing replay protection for mobile API requests | no nonce/timestamp/signature | include signed nonce + timestamp validation | Mobile (Kotlin/Swift) | CWE-294 | Prevent request replay abuse. |
|
|
33
|
+
| MOB-031 | Local cache stores decrypted payload indefinitely | decrypted JSON persisted in cache | encrypt cache or store minimal references | Mobile (Kotlin/Swift) | CWE-312 | Protect local cache confidentiality. |
|
|
34
|
+
| MOB-032 | Insecure fallback to HTTP on TLS errors | retry via http:// when HTTPS fails | fail closed and surface secure error | Mobile (Kotlin/Swift) | CWE-319 | Never downgrade secure transport. |
|
|
35
|
+
| MOB-033 | Unsafe custom URLSession delegate accepts any cert | trust challenge blindly | strict trust evaluation + pinning | Mobile (Kotlin/Swift) | CWE-295 | Enforce robust TLS validation. |
|
|
36
|
+
| MOB-034 | App integrity checks disabled behind feature flag | remote flag can disable integrity | integrity checks must be local and mandatory | Mobile (Kotlin/Swift) | CWE-693 | Prevent remote disabling of protections. |
|
|
37
|
+
| MOB-035 | Token refresh race creates stale token reuse | concurrent refresh requests overwrite state | single-flight refresh with atomic token swap | Mobile (Kotlin/Swift) | CWE-367 | Synchronize token renewal safely. |
|
|
38
|
+
| MOB-036 | Keychain item access class too permissive | `kSecAttrAccessibleAlways` for secrets | use `WhenUnlockedThisDeviceOnly` where possible | Mobile (Kotlin/Swift) | CWE-732 | Tighten keychain accessibility scope. |
|
|
39
|
+
| MOB-037 | Android backup includes sensitive app data | `allowBackup=true` with tokens stored locally | disable backup or exclude secret files | Mobile (Kotlin/Swift) | CWE-312 | Avoid secret exfil via backups. |
|
|
40
|
+
| MOB-038 | Obfuscation/minification disabled in release | reverse engineering made trivial | enable R8/ProGuard and symbol stripping | Mobile (Kotlin/Swift) | CWE-656 | Reduce static analysis attack surface. |
|
|
41
|
+
| MOB-039 | Binary lacks jailbreak/root hook detection for runtime tampering | no Frida/hook heuristics | detect hooks/instrumentation and restrict operations | Mobile (Kotlin/Swift) | CWE-693 | Add runtime anti-tamper controls. |
|
|
42
|
+
| MOB-040 | Secrets in hardcoded strings/resources | API keys in `strings.xml`/Swift constants | move secrets to backend and ephemeral exchange | Mobile (Kotlin/Swift) | CWE-798 | Eliminate embedded long-term secrets. |
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Node.js / NestJS
|
|
2
|
+
|
|
3
|
+
## Stack overview
|
|
4
|
+
|
|
5
|
+
**Node.js LTS 18/20/22** with **NestJS** / Express patterns: validation pipes, ORM raw queries, CORS, JWT, throttling, and logging. Metrics are prefixed **`NST`**.
|
|
6
|
+
|
|
7
|
+
## Top threats
|
|
8
|
+
|
|
9
|
+
- Prototype pollution and CORS (`NST-001`, `NST-002`).
|
|
10
|
+
- SQL/Prisma injection and SSRF (`NST-004`, `NST-005`, `NST-014`).
|
|
11
|
+
- AuthZ and guard mistakes (`NST-006`, `NST-008`, `NST-015`, `NST-016`).
|
|
12
|
+
|
|
13
|
+
## Pattern catalog
|
|
14
|
+
|
|
15
|
+
Complete Anti-Pattern / Safe-Pattern definitions live in [`patterns.md`](patterns.md). The table below is a **table of contents** by metric ID.
|
|
16
|
+
|
|
17
|
+
| ID | Metric | Stack |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `NST-001` | Prototype Pollution в DTO merge | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
20
|
+
| `NST-002` | Insecure CORS (`origin: *`) | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
21
|
+
| `NST-003` | Missing global ValidationPipe | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
22
|
+
| `NST-004` | TypeORM SQL Injection (query string concat) | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
23
|
+
| `NST-005` | Prisma Raw Injection (`$queryRawUnsafe`) | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
24
|
+
| `NST-006` | Open Redirect in controller | `CWE-601` |
|
|
25
|
+
| `NST-007` | Hardcoded secrets in source | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
26
|
+
| `NST-008` | JWT verify without algorithm allowlist | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
27
|
+
| `NST-009` | Missing body size limits | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
28
|
+
| `NST-010` | Verbose exception leak | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
29
|
+
| `NST-011` | Info leak in Swagger DTO | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
30
|
+
| `NST-012` | Unsafe implicit type conversion | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
31
|
+
| `NST-013` | Raw HTML in template rendering | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
32
|
+
| `NST-014` | SSRF in `HttpService` | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
33
|
+
| `NST-015` | Missing rate limiting in root module | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
34
|
+
| `NST-016` | Insecure Reflector usage in Guard | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
35
|
+
| `NST-017` | File upload without magic number check | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
36
|
+
| `NST-018` | Insecure bcrypt rounds | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
37
|
+
| `NST-019` | XXE risk in xml2js parsing | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
38
|
+
| `NST-020` | Log Injection | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
39
|
+
| `NST-021` | CSV Injection in Node/NestJS export handlers (CWE-1236) | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
40
|
+
| `NST-022` | Debug message disclosure in production exception filter (CWE-1295) | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
41
|
+
| `NST-023` | CSV export from untrusted DTO fields without normalization (CWE-1236) | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
42
|
+
| `NST-024` | `localStorage` с access/refresh токенами (CWE-312) | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
43
|
+
| `NST-025` | PII в `localStorage` как JSON (CWE-312) | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
44
|
+
| `NST-026` | Refresh token в `sessionStorage` без ротации (CWE-532) | Validate data with Zod and sanitize DOM/HTML sinks with DOMPurify before rendering. |
|
|
45
|
+
|
|
46
|
+
## Verification
|
|
47
|
+
|
|
48
|
+
**Verification:** Check the gold testbed file(s) below for `Vulnerable: <ID>` markers (static Semgrep + `detection-matrix.md` ground truth).
|
|
49
|
+
|
|
50
|
+
- [`gold-standard-testbed/nestjs_vulnerable.ts`](../gold-standard-testbed/nestjs_vulnerable.ts)
|
|
51
|
+
|
|
52
|
+
After changing [`patterns.md`](patterns.md), run from the repo root:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
python scripts/sync_semgrep.py
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Workflow: Recon → Scan → Verify
|
|
59
|
+
|
|
60
|
+
### 1) Recon
|
|
61
|
+
- Map entrypoints, data flows, and trust boundaries for this stack.
|
|
62
|
+
- Identify which metrics in [`patterns.md`](patterns.md) apply to the code under review.
|
|
63
|
+
|
|
64
|
+
### 2) Scan
|
|
65
|
+
- Run Semgrep with `semgrep-rules/<skill>.yaml` (generated) and correlate with Anti-Patterns.
|
|
66
|
+
- Eliminate findings that cannot bind to a metric row.
|
|
67
|
+
|
|
68
|
+
### 3) Verify
|
|
69
|
+
- Confirm markers or scanner hits for touched IDs in the gold testbed when adding metrics.
|
|
70
|
+
- Emit findings as `Vulnerable: <PREFIX>-<NNN>` in written reviews.
|
|
71
|
+
|