VNX-1322: Use of Blocking Code in Single-threaded, Non-blocking Context
Reports synchronous Node.js APIs (fs.*Sync, execSync/spawnSync, pbkdf2Sync/scryptSync, zlib *Sync, bcrypt hashSync/compareSync) called inside an HTTP request handler, where they stall the event loop for every client. Startup code, helpers outside handlers, async APIs, scripts and tests are not reported.
| Field | Value |
|---|---|
| Rule ID | VNX-1322 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | JavaScript / TypeScript |
| CWE | CWE-1322 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | blocking-in-non-blocking event-loop dos cwe-1322 |
| Source | internal/sast/rules/vnx-1322.rego |
Overview
VNX-1322 maps to CWE-1322: Use of Blocking Code in Single-threaded, Non-blocking Context. A Node.js server runs every request on one thread. A synchronous file, process, key-derivation or compression call inside a request handler stops that thread until it returns, so every other client waits.
Why This Matters
A single slow disk read or child process inside a handler turns into server-wide latency, and an attacker who can trigger the call repeatedly (a large file, an expensive pbkdf2Sync) can deny service to everyone with a handful of requests.
What Gets Flagged
The rule looks for a sync API inside a function whose parameters are (req, res), (request, reply), (request, response), (request, h) or a Koa ctx, including http.createServer callbacks and TypeScript-typed parameters.
router.get('/docs/:name', (req, res) => {
const body = fs.readFileSync(file, 'utf8') // FLAGGED
res.send(body)
})
this.handleLogin = function(req, res, next) {
var key = crypto.pbkdf2Sync(req.body.password, salt, 210000, 64, "sha512") // FLAGGED
}
convert.post('/convert', async (req: Request, res: Response) => {
const out = execSync(`soffice --convert-to pdf ${input}`) // FLAGGED
})
What Is Not Flagged
- Sync calls at module level or in a helper outside any handler (loading certificates or config at startup, warming a cache).
fs.promises.*, callback-stylefs.readFile,execFilewith a callback.- Build scripts and CLIs, test files, comments and string literals.
- A sync call reached from a handler through a helper function defined elsewhere is not followed.
Remediation
- Use the asynchronous form:
await fs.promises.readFile(...),execFile(..., cb)orutil.promisify(exec),crypto.pbkdf2(..., cb),zlib.gzip(..., cb),bcrypt.compare(...). - Move unavoidable synchronous work to startup, or to a worker thread.
References
- CWE-1322: Use of Blocking Code in Single-threaded, Non-blocking Context
- Node.js: Don’t Block the Event Loop
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-1322
// nosec vnx-1322
Or record an ignore rule: vulnetix ignore add --rule vnx-1322 --reason "...". See Suppressing findings.