Skip to content

Commit 661c832

Browse files
committed
fix: Refine spring-analyzer post wording
1 parent fe64a81 commit 661c832

1 file changed

Lines changed: 6 additions & 6 deletions

File tree

src/content/blog/spring-analyzer.mdx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ The engine traces the complete path: `@RequestBody RenderRequest` → `renderFro
8181

8282
### Configuration-Aware Sinks
8383

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.
8585

8686
Consider a controller that passes user-controlled template content to two different Freemarker services:
8787

@@ -109,7 +109,7 @@ this.templateConfig.setNewBuiltinClassResolver(TemplateClassResolver.UNRESTRICTE
109109
this.templateConfig.setNewBuiltinClassResolver(TemplateClassResolver.ALLOWS_NOTHING_RESOLVER);
110110
```
111111

112-
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.
113113

114114
## Cross-Endpoint Flows
115115

@@ -182,7 +182,7 @@ public ResponseEntity<String> getMessageContent(@PathVariable Long id) {
182182
}
183183
```
184184

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.
186186

187187
### Through Service State
188188

@@ -221,7 +221,7 @@ public ResponseEntity<String> getLastContent() {
221221
}
222222
```
223223

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.
225225

226226
### Column-Level Precision
227227

@@ -264,15 +264,15 @@ public Message(String title, String content, String author) {
264264
}
265265
```
266266

267-
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:
268268

269269
- `GET /api/messages/{id}/content` → returns raw `content`**XSS detected**
270270
- `GET /api/messages/{id}/title` → returns raw `title`**XSS detected**
271271
- `GET /api/messages/{id}/author` → returns escaped `author`**no finding**
272272

273273
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.
274274

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.
276276

277277
## Limitations
278278

0 commit comments

Comments
 (0)