You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/blog/spring-analyzer.mdx
+6-6Lines changed: 6 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -81,7 +81,7 @@ The engine traces the complete path: `@RequestBody RenderRequest` → `renderFro
81
81
82
82
### Configuration-Aware Sinks
83
83
84
-
Not every call to a template engine is equally dangerous. Whether user input reaching `template.process()` constitutes an SSTI depends on how the engine is configured — and production codebases frequently have both hardened and default configurations side by side. An analyzer that doesn't track configuration state either flags both (noise) or misses both.
84
+
Not every call to a template engine is equally dangerous. Whether user input reaching `template.process()` constitutes an SSTI depends on how the engine is configured. An analyzer that doesn't distinguish between hardened and default configurations either flags both (noise) or misses both.
85
85
86
86
Consider a controller that passes user-controlled template content to two different Freemarker services:
We resolve `@Autowired` bean constructors and track configuration state. We flag the marketing service — `UNRESTRICTED_RESOLVER` allows class loading, enabling remote code execution — and suppress the notification service, where `ALLOWS_NOTHING_RESOLVER` prevents class instantiation. Getting this distinction wrong means noise or missed vulnerabilities.
112
+
The engine resolves `@Autowired` bean constructors and tracks configuration state. It flags the marketing service — `UNRESTRICTED_RESOLVER` allows class loading, enabling remote code execution — and suppresses the notification service, where `ALLOWS_NOTHING_RESOLVER` prevents class instantiation.
113
113
114
114
## Cross-Endpoint Flows
115
115
@@ -182,7 +182,7 @@ public ResponseEntity<String> getMessageContent(@PathVariable Long id) {
182
182
}
183
183
```
184
184
185
-
The two endpoints share no direct method call. We trace the full flow across both by modeling JPA repository operations as database read/write boundaries. When an entity is persisted via `repository.save()`, the taint state of each field is recorded against that entity type. When a different endpoint retrieves via `repository.findById()`, we look up the stored state and propagate it per-column to the retrieved entity's fields. No actual database connection is needed — this is a static approximation of persistence-layer data flow.
185
+
The two endpoints share no direct method call. The engine traces the full flow across both by modeling JPA repository operations as database read/write boundaries. When an entity is persisted via `repository.save()`, the taint state of each field is recorded against that entity type. When a different endpoint retrieves via `repository.findById()`, the engine looks up the stored state and propagates it per-column to the retrieved entity's fields. No actual database connection is needed — this is a static approximation of persistence-layer data flow.
186
186
187
187
### Through Service State
188
188
@@ -221,7 +221,7 @@ public ResponseEntity<String> getLastContent() {
221
221
}
222
222
```
223
223
224
-
We trace the data from `createMessage`'s `content` parameter through the `lastContent` field assignment and back out via `getLastContent()` — a cross-endpoint stored XSS that doesn't touch the database at all.
224
+
The engine traces the data from `createMessage`'s `content` parameter through the `lastContent` field assignment and back out via `getLastContent()` — a cross-endpoint stored XSS that doesn't touch the database at all.
The `author` field is HTML-escaped before it reaches the database. The `title` and `content` fields are stored raw. We track each column independently:
267
+
The `author` field is HTML-escaped before it reaches the database. The `title` and `content` fields are stored raw. The engine tracks each column independently:
268
268
269
269
-`GET /api/messages/{id}/content` → returns raw `content` → **XSS detected**
270
270
-`GET /api/messages/{id}/title` → returns raw `title` → **XSS detected**
Without column-level tracking, the choice is between flagging all three endpoints (false positives on `author`) or suppressing the entire entity (missing real vulnerabilities on `content` and `title`). Per-column sensitivity avoids the trade-off.
274
274
275
-
We also recognize sanitizers applied at read time. The `GET /api/messages/{id}/content/safe` endpoint passes content through `HtmlUtils.htmlEscape()` before returning it — the engine sees the sanitizer and suppresses the finding for that path as well.
275
+
The engine also recognizes sanitizers applied at read time. The `GET /api/messages/{id}/content/safe` endpoint passes content through `HtmlUtils.htmlEscape()` before returning it — it sees the sanitizer and suppresses the finding for that path as well.
0 commit comments