JS 1.0 · Project evidence

JS Todo App (SQLite 4): Find Vulnerabilities

Todo app with SQLite and file attachments. Scaffolded from Cursor's Auto MAX mode.

Project purpose and scale

JS Todo App (SQLite 4): Find Vulnerabilities

Todo app with SQLite and file attachments. Scaffolded from Cursor's Auto MAX mode.

Source files
8
Reference findings
7
Matched signatures
13
Unmatched signatures
43

Reference context

Deterministic reference findings

These findings define the project reference set for JS 1.0. Their presence does not establish exhaustive ground truth.

ClassSeverityLocationReference ID
information-exposuremediumserver.js:7js-xpowered-by-header-4
allocation-of-resources-without-limits-or-throttlingmediumserver.js:106js-alloc-without-limits-4a
allocation-of-resources-without-limits-or-throttlingmediumserver.js:168js-alloc-without-limits-4b
allocation-of-resources-without-limits-or-throttlingmediumserver.js:183js-alloc-without-limits-4c
path-traversalhighserver.js:140js-path-traversal-4a
path-traversalhighserver.js:146js-path-traversal-4b
path-traversalhighserver.js:174js-path-traversal-4c

Finding evidence

Reference-matched normalized signatures

Matched means agreement with the Snyk Code reference set, not independent ground-truth adjudication.

Severity and description show the deterministic most-frequent metadata variant; the final column records how many variants were observed across repetitions.

ConfigurationClassRepresentative severityLocationRecurrenceRepresentative descriptionMetadata variants
Claude Opus 4.6 Highallocation-of-resources-without-limits-or-throttlingmediumserver.js:1065 of 5Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.1
Claude Opus 4.6 Highinformation-exposuremediumserver.js:75 of 5Disable X-Powered-By header for your Express app (consider using Helmet middleware), because it exposes information about the used framework to potential attackers.1
Claude Opus 4.6 Mediumallocation-of-resources-without-limits-or-throttlingmediumserver.js:1065 of 5Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.1
Claude Opus 4.6 Mediuminformation-exposuremediumserver.js:75 of 5Disable X-Powered-By header for your Express app (consider using Helmet middleware), because it exposes information about the used framework to potential attackers.1
Claude Opus 4.7 Maxallocation-of-resources-without-limits-or-throttlingmediumserver.js:1065 of 5Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.1
Claude Opus 4.7 Maxallocation-of-resources-without-limits-or-throttlingmediumserver.js:1681 of 5Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.1
Claude Opus 4.7 Maxinformation-exposuremediumserver.js:75 of 5Disable X-Powered-By header for your Express app (consider using Helmet middleware), because it exposes information about the used framework to potential attackers.1
Claude Opus 4.7 Maxpath-traversalhighserver.js:1401 of 5Unsanitized input from an HTTP parameter flows into fs.unlink, where it is used as a path. This may result in a Path Traversal vulnerability and allow an attacker to delete arbitrary files.1
Claude Sonnet 4.6 Highallocation-of-resources-without-limits-or-throttlingmediumserver.js:1065 of 5Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.1
Claude Sonnet 4.6 Highinformation-exposuremediumserver.js:75 of 5Disable X-Powered-By header for your Express app (consider using Helmet middleware), because it exposes information about the used framework to potential attackers.1
Claude Sonnet 4.6 Mediumallocation-of-resources-without-limits-or-throttlingmediumserver.js:1065 of 5Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.1
Claude Sonnet 4.6 Mediumallocation-of-resources-without-limits-or-throttlingmediumserver.js:1682 of 5Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.1
Claude Sonnet 4.6 Mediuminformation-exposuremediumserver.js:75 of 5Disable X-Powered-By header for your Express app (consider using Helmet middleware), because it exposes information about the used framework to potential attackers.1

Finding evidence

Unmatched normalized signatures

Unmatched reports may be false-positive-shaped, adjacent, or likely product-gap candidates.

Severity and description show the deterministic most-frequent metadata variant; the final column records how many variants were observed across repetitions.

ConfigurationClassRepresentative severityLocationRecurrenceRepresentative descriptionMetadata variants
Claude Opus 4.6 Highsql-injectioncriticalserver.js:345 of 5The deleteTodo function concatenates req.params.id directly into a SQL string ('DELETE FROM todos WHERE id = ' + id) without parameterization. The DELETE route (line 174) passes the unvalidated req.params.id string to this function, allowing an attacker to inject arbitrary SQL via the :id URL parameter.5
Claude Opus 4.6 Mediumsql-injectioncriticalserver.js:345 of 5deleteTodo uses string concatenation ('DELETE FROM todos WHERE id = ' + id) instead of parameterized queries, allowing SQL injection via the id parameter.5
Claude Opus 4.6 Mediumsql-injectioncriticalserver.js:1742 of 5The DELETE /api/todos/:id route passes req.params.id directly to q.deleteTodo() without validating it as an integer, enabling SQL injection through the string-concatenated query.2
Claude Opus 4.7 Maxcsrfmediumserver.js:402 of 5No CSRF protection on state-changing endpoints (POST/PUT/DELETE /api/todos). The app uses multipart/form-data which browsers permit cross-origin, and there is no SameSite cookie, origin/referer check, or CSRF token.2
Claude Opus 4.7 Maxcsrfmediumserver.js:853 of 5No CSRF protection on state-changing endpoints (POST/PUT/DELETE /api/todos). Endpoints accept multipart/form-data and JSON without any token, origin, or SameSite cookie check, allowing cross-site request forgery.3
Claude Opus 4.7 Maxidorhighserver.js:641 of 5There is no authentication or authorization on any endpoint. Any client can list, read, modify, delete, and download attachments of any todo by guessing/iterating numeric ids.1
Claude Opus 4.7 Maxidormediumserver.js:731 of 5All endpoints lack any authentication or authorization. Any user can read, modify, delete, or download attachments for any todo by guessing/iterating sequential numeric IDs.1
Claude Opus 4.7 Maxidormediumserver.js:1811 of 5There is no authentication or authorization layer. Any client can enumerate todo IDs and read/modify/delete other users' todos or download their attachments via /api/todos/:id and /api/todos/:id/attachment.1
Claude Opus 4.7 Maximproper-code-sanitizationlowserver.js:141 of 5Stored filename uses path.extname(file.originalname) directly. While Date.now-based prefix prevents collisions, the attacker-controlled extension is preserved and could later be served back via res.download with a misleading Content-Type/filename, or be problematic if the uploads directory is ever served statically.1
Claude Opus 4.7 Maximproper-code-sanitizationlowserver.js:1892 of 5res.download is called with row.attachment_original_name (user-controlled at upload time) as the download filename. While Express encodes the Content-Disposition header, the unsanitized original name is round-tripped to clients and may be used in subsequent processing without normalization.2
Claude Opus 4.7 Maximproper-type-validationlowserver.js:861 of 5POST /api/todos does not validate the types of req.body fields (title, description, completed) before passing them to the database — non-string types are accepted and stored without proper coercion.1
Claude Opus 4.7 Maximproper-type-validationlowserver.js:1075 of 5PUT /api/todos/:id does not validate that req.params.id is an integer; while the prepared statements are parameterized, lack of validation is inconsistent with other handlers and could mask logic errors.5
Claude Opus 4.7 Maximproper-type-validationhighserver.js:1673 of 5DELETE /api/todos/:id does not validate that req.params.id is an integer (unlike GET handler at line 74). Combined with the concatenated SQL in deleteTodo, this enables SQL injection.3
Claude Opus 4.7 Maxinformation-exposurelowserver.js:1891 of 5res.download uses attachment_original_name (attacker-controlled at upload time) directly in the Content-Disposition header. Express sanitizes the header, but the original filename is fully attacker-controlled and reflected to other users who download the attachment, enabling filename spoofing/social engineering.1
Claude Opus 4.7 Maxinsecure-transportlowserver.js:1951 of 5Server listens over plain HTTP with no TLS, HSTS, or secure-cookie configuration; attachments and todo data traverse the network in cleartext.1
Claude Opus 4.7 Maxotherhighpackage.json:141 of 5Vulnerable dependency: multer ^1.4.5-lts.1 has known DoS / unbounded-resource-consumption advisories (e.g., GHSA-g5hg-p3ph-g8qg, GHSA-fjgf-rc76-4x9p). Should be upgraded to multer 2.x.1
Claude Opus 4.7 Maxothermediumserver.js:111 of 5Unrestricted file upload type: multer storage has no fileFilter and no MIME/extension allowlist. Attackers can upload arbitrary file types (HTML, SVG, executables). Because path.extname(file.originalname) is preserved in the stored filename and originalname is returned in res.download Content-Disposition, this can facilitate further attacks (e.g., serving HTML/SVG with XSS if served as static, or distributing malware).1
Claude Opus 4.7 Maxothermediumserver.js:641 of 5No authentication or authorization on any endpoint. All todos and attachments are globally readable, modifiable, and deletable by any client, constituting broken access control.1
Claude Opus 4.7 Maxsql-injectioncriticalserver.js:345 of 5deleteTodo builds the DELETE query via string concatenation with req.params.id (used at line 174 without integer validation), allowing arbitrary SQL injection via the DELETE /api/todos/:id endpoint.5
Claude Sonnet 4.6 Highcsrflowserver.js3 of 5All state-mutating endpoints (POST /api/todos, PUT /api/todos/:id, DELETE /api/todos/:id) lack CSRF tokens or SameSite cookie enforcement. If a session/cookie-based authentication mechanism is added in the future, all these endpoints will be immediately exploitable via cross-site request forgery.3
Claude Sonnet 4.6 Highidorhighserver.js2 of 5There is no authentication or authorization layer. Any unauthenticated client can enumerate, read, update, or delete any todo record by guessing sequential integer IDs, constituting an Insecure Direct Object Reference vulnerability.2
Claude Sonnet 4.6 Highidorhighserver.js:641 of 5No authentication or authorization is implemented on any API endpoint. Any unauthenticated client can enumerate, read, create, update, or delete all todos by simply guessing or iterating numeric IDs, constituting a classic Insecure Direct Object Reference vulnerability.1
Claude Sonnet 4.6 Highimproper-type-validationhighserver.js:1073 of 5The PUT /api/todos/:id handler reads `req.params.id` without validating that it is an integer (unlike the GET handlers at lines 74 and 182 which call `Number.isInteger`). A non-numeric or crafted string reaches both the SQL-injection-vulnerable `deleteTodo` path and the dynamic UPDATE statement, enabling injection and unexpected behaviour.3
Claude Sonnet 4.6 Highimproper-type-validationhighserver.js:1674 of 5The DELETE /api/todos/:id handler assigns `id = req.params.id` without validating it is an integer (unlike the GET handlers at lines 74–75 which call Number.isInteger). This non-validated string is passed directly to the SQL-injection-vulnerable `deleteTodo` function.4
Claude Sonnet 4.6 Highotherlowserver.js1 of 5No HTTP security headers are set (no Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, or Strict-Transport-Security). This leaves the application exposed to clickjacking, MIME-sniffing, and XSS escalation attacks.1
Claude Sonnet 4.6 Highothermediumserver.js:111 of 5The multer configuration has no fileFilter callback to restrict allowed file types or MIME types. Attackers can upload executable scripts, malware, or other dangerous file types. While files are served with Content-Disposition: attachment, clients who open them locally are at risk.1
Claude Sonnet 4.6 Highothermediumserver.js:184 of 5Multer has no `fileFilter` configured, so any file type (executables, HTML files with scripts, SVGs, etc.) can be uploaded and stored on the server. While files are not directly served statically from the uploads directory, this widens the attack surface, especially if the upload directory is ever misconfigured or future code serves those files.4
Claude Sonnet 4.6 Highsql-injectioncriticalserver.js:345 of 5The deleteTodo function builds a DELETE query by directly concatenating the user-supplied `id` parameter into the SQL string (`"DELETE FROM todos WHERE id = " + id`). Every other query in the file uses parameterized placeholders (`?`), but this one does not, allowing an attacker to inject arbitrary SQL via the route parameter (e.g. `DELETE /api/todos/1 OR 1=1`).5
Claude Sonnet 4.6 Mediumcsrfhighserver.js3 of 5All mutating endpoints (POST /api/todos, PUT /api/todos/:id, DELETE /api/todos/:id) lack any CSRF protection. There is no CSRF token validation, no SameSite cookie policy, and no CORS restriction, enabling cross-site request forgery attacks from any origin.3
Claude Sonnet 4.6 Mediumcsrfmediumserver.js:401 of 5No CSRF protection is implemented. The API accepts state-changing requests (POST, PUT, DELETE) without any CSRF token validation. Combined with the lack of authentication, cross-origin requests from malicious pages can manipulate todos on behalf of any user sharing the server.1
Claude Sonnet 4.6 Mediumcsrfmediumserver.js:851 of 5State-changing API endpoints (POST /api/todos, PUT /api/todos/:id, DELETE /api/todos/:id) have no CSRF protection—no CSRF tokens, no Origin/Referer header validation, and no SameSite cookie policy. A malicious third-party site can trigger cross-site requests on behalf of authenticated users.1
Claude Sonnet 4.6 Mediumidormediumserver.js1 of 5There is no authentication or authorization mechanism. Any unauthenticated user can read, create, modify, or delete any todo record by guessing or enumerating integer IDs, constituting an Insecure Direct Object Reference vulnerability.1
Claude Sonnet 4.6 Mediumidorhighserver.js:641 of 5No authentication or authorization is implemented on any API endpoint. Any unauthenticated user can list, view, create, update, or delete any todo item and download any attachment. All resource IDs are enumerable sequential integers.1
Claude Sonnet 4.6 Mediumidorhighserver.js:1661 of 5There is no authentication or authorization mechanism anywhere in the application. Any unauthenticated user can read, create, modify, or delete any todo item by iterating or guessing numeric IDs via the REST API (GET/PUT/DELETE /api/todos/:id).1
Claude Sonnet 4.6 Mediumimproper-type-validationhighserver.js:1071 of 5The PUT /api/todos/:id endpoint does not validate or convert the id parameter to an integer (unlike the GET endpoint at line 74), passing an unvalidated string to multiple database queries and enabling potential SQL injection and IDOR.1
Claude Sonnet 4.6 Mediumimproper-type-validationcriticalserver.js:1671 of 5The DELETE /api/todos/:id endpoint reads req.params.id as a raw string with no integer validation and passes it directly to the string-concatenating deleteTodo() function, enabling SQL injection.1
Claude Sonnet 4.6 Mediuminsecure-transportlowserver.js:1952 of 5The server listens on plain HTTP with no HTTPS enforcement or redirect. All data, including any future authentication credentials, is transmitted in cleartext.2
Claude Sonnet 4.6 Mediumotherhighserver.js1 of 5No authentication or authorization is implemented on any endpoint. Any unauthenticated user can read all todos, create/modify/delete any todo, and download all attachments.1
Claude Sonnet 4.6 Mediumotherlowserver.js:71 of 5No HTTP security headers are set (Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Strict-Transport-Security). This exposes the application to clickjacking, MIME-type sniffing attacks, and weakens defenses against XSS.1
Claude Sonnet 4.6 Mediumotherhighserver.js:111 of 5Multer has no fileFilter configured, so any file type (including .js, .sh, .php, .exe, .html) can be uploaded to the server. If the uploads directory is ever served statically or executed, this enables remote code execution or stored XSS via uploaded files.1
Claude Sonnet 4.6 Mediumotherhighserver.js:183 of 5Multer has no fileFilter callback and no MIME type or extension restrictions. Any file type can be uploaded (e.g., server-side scripts, HTML, executables), potentially enabling malicious file uploads.3
Claude Sonnet 4.6 Mediumothermediumserver.js:1891 of 5res.download() is called with the user-supplied attachment_original_name as the download filename, which is set in the Content-Disposition response header. If the filename contains CR/LF characters (\r\n), it can cause HTTP response header injection, allowing an attacker to inject arbitrary headers or split the response.1
Claude Sonnet 4.6 Mediumsql-injectioncriticalserver.js:345 of 5deleteTodo builds the SQL DELETE statement by directly concatenating the user-supplied id parameter (req.params.id, a raw URL string that is never validated as an integer) into the query string: `"DELETE FROM todos WHERE id = " + id`. An attacker can supply a value like `1 OR 1=1` to delete all rows, or craft other SQL expressions to manipulate data.5

Model behavior

Recurrence by configuration

Stable recurrence measures repeated behavior, not independent validity.

ConfigurationMatched signaturesMatched all fiveUnmatched signaturesUnmatched all five
Claude Opus 4.6 High2211
Claude Opus 4.6 Medium2221
Claude Opus 4.7 Max42162
Claude Sonnet 4.6 High2291
Claude Sonnet 4.6 Medium32151

Inspectable source context

Reference locations

Apache-licensed fixture excerpts are shown around declared reference lines. They are evidence, not runnable examples.

server.js:7js-xpowered-by-header-4
const { openDb, UPLOADS_DIR } = require("./db");

const app = express();
const db = openDb();
const PORT = process.env.PORT || 3000;
server.js:106js-alloc-without-limits-4a
});

app.put("/api/todos/:id", upload.single("attachment"), (req, res) => {
  const id = req.params.id;
server.js:168js-alloc-without-limits-4b
app.delete("/api/todos/:id", (req, res) => {
  const id = req.params.id;
  try {
    const row = q.getStoredAttachmentOnly.get(id);
    if (!row) return res.status(404).json({ error: "Not found" });
server.js:183js-alloc-without-limits-4c
app.get("/api/todos/:id/attachment", (req, res) => {
  const id = Number(req.params.id);
  if (!Number.isInteger(id)) return res.status(400).json({ error: "Invalid id" });
  try {
    const row = q.getAttachmentForDownload.get(id);
server.js:140js-path-traversal-4a
        fs.unlink(path.join(UPLOADS_DIR, existing.attachment_stored_name), () => {});
      }
      updates.push("attachment_original_name = ?", "attachment_stored_name = ?");
      values.push(req.file.originalname, req.file.filename);
    } else if (req.body.removeAttachment === true || req.body.removeAttachment === "true") {
server.js:146js-path-traversal-4b
        fs.unlink(path.join(UPLOADS_DIR, existing.attachment_stored_name), () => {});
      }
      updates.push("attachment_original_name = ?", "attachment_stored_name = ?");
      values.push(null, null);
    }
server.js:174js-path-traversal-4c
      fs.unlink(path.join(UPLOADS_DIR, row.attachment_stored_name), () => {});
    }
    q.deleteTodo(id);
    res.status(204).send();
  } catch (err) {

Interpretation boundary

Inspect divergence before classifying it

Unmatched reports may be false-positive-shaped, adjacent to the release reference scope, or likely valid product-gap candidates. Missed reference findings may expose representative-versus-systematic coverage behavior.

Return to explorer evidence