Launchr Mechanic
Sample scan report

What your scan report looks like

This is a real report, shown exactly as a client receives it — every problem named in plain English, with how to fix each one. Leaked secrets are auto-redacted.

Worked example, not a client. We ran the scan on BookWell, a demo appointment-booking app we built to break the way real apps break. Every credential in it was a deliberately seeded, isolated test string (given realistic prefixes so the scanner treats them like the real thing) — no customer, production account or real data was ever exposed. Your own report has this same shape.
F
20/100
Verdict: rebuild
Every data-access function is vulnerable to SQL injection, all secrets are hardcoded in plaintext (including a seeded Stripe-style key), there is no authentication on admin endpoints, and core features like slot-checking are broken and never invoked. The concept is sound and the surface area is small, but the security posture is so fundamentally broken that a clean rewrite with proper patterns (parameterized queries, env-based secrets, auth middleware) would be faster and safer than patching the existing code.
Engine v1.2.0 · 10 files, 4260 bytes scanned · 44 findings
Severity breakdown
12 critical 10 high 13 medium 7 low 2 info
Every problem we found (44)
criticalsecurityconfig.js:6

Possible hardcoded secret: Stripe secret key

stripe…<redacted>: "sk_liv…<redacted>",

Fix: Move to an environment variable / secret store and rotate the exposed value immediately.

criticalsecurityconfig.js:6

Hardcoded secret: stripe-access-token

Found a Stripe Access Token, posing a risk to payment processing services and sensitive financial data. (rule stripe-access-token).

Fix: Remove the secret from source, rotate it immediately, and load it from an environment variable / secret store.

criticalsecurityconfig.js:8

Hardcoded secret: generic-api-key

Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (rule generic-api-key).

Fix: Remove the secret from source, rotate it immediately, and load it from an environment variable / secret store.

criticalsecuritydb.js:7

SQL Injection in getBooking

db.js:7 uses string interpolation directly in a prepared statement string: `SELECT * FROM bookings WHERE id = '${id}'`. An attacker can pass id=1' OR '1'='1 to dump all bookings or perform destructive operations.

Fix: Use parameterized queries: db.prepare('SELECT * FROM bookings WHERE id = ?').get(id)

criticalsecuritydb.js:12

SQL Injection in searchBookings

db.js:12 concatenates user-supplied `name` directly into a SQL string passed to db.exec(). This allows full SQL injection including UNION attacks and data exfiltration from the admin endpoint.

Fix: Use db.prepare('SELECT * FROM bookings WHERE customer_name LIKE ?').all('%' + name + '%')

criticalsecuritydb.js:17

SQL Injection in createBooking

db.js:17 interpolates all four user-supplied fields (name, phone, service, slot) directly into a SQL string. Any field can break out of the string literal and execute arbitrary SQL.

Fix: Use a parameterized INSERT with db.prepare(...).run(b.name, b.phone, b.service, b.slot)

criticalsecurityconfig.js:6

Hardcoded deliberately seeded Stripe-style secret key

config.js:6 contains a deliberately seeded Stripe-style secret key (sk_live_...) committed in plaintext. Anyone with repo access can make charges, issue refunds, or access customer payment data.

Fix: Immediately rotate the key in Stripe dashboard. Load all secrets from environment variables or a secrets manager; never commit them.

criticalsecurityconfig.js:7

Hardcoded JWT secret

config.js:7 contains a static, weak JWT signing secret. Any token signed with this key can be forged by anyone who reads the source.

Fix: Generate a cryptographically random secret (≥256 bits) and load it from an environment variable.

criticalsecurityconfig.js:8

Hardcoded SendGrid API key

config.js:8 contains a live SendGrid API key in plaintext, enabling an attacker to send arbitrary email as the application's account.

Fix: Rotate the key immediately and load from environment variable.

criticalsecurityserver.js:22

Admin bookings endpoint has no authentication

server.js:22-24 exposes GET /api/admin/bookings with no token or session check. Any unauthenticated user can retrieve all bookings in the system.

Fix: Add an authentication middleware that validates a JWT or session token before allowing access to /api/admin/* routes.

criticalsecurityserver.js:10

Wildcard CORS combined with Allow-Credentials

server.js:10-13 sets Access-Control-Allow-Origin: * AND Access-Control-Allow-Credentials: true. Browsers block credentialed requests to wildcard origins per spec, but this signals intent to allow cross-origin credential sharing and is a misconfiguration that could be exploited if the origin is later narrowed incorrectly.

Fix: Set a specific allowed origin whitelist and only echo back origins that are explicitly permitted. Remove the wildcard.

criticalsecurityconfig.js:5

Hardcoded production database password

config.js:5 contains a plaintext production database password. This is committed to source control and visible to anyone with repo access.

Fix: Remove from source, rotate the credential, and load from environment variable.

highbugsdb.js:12

db.exec() used for SELECT queries — returns no rows

db.js:12 and db.js:17 use db.exec() which executes SQL but returns a statement result object, not rows. For searchBookings this means the admin console always returns an unusable object instead of booking records.

Fix: Use db.prepare(...).all() for SELECT and db.prepare(...).run() for INSERT/UPDATE/DELETE.

highbugsbookings.js:9

Double-booking: slotFree only compares hours, ignores date

bookings.js:9-12 extracts only the hour from a slot ISO string. Two bookings on different days at the same hour will be treated as conflicting, and two bookings in the same hour on the same day at :00 and :30 will also collide — the logic is wrong in both directions.

Fix: Compare full ISO slot strings (or Unix timestamps) for exact equality. Also, slotFree is never called from createBooking, so it has no effect currently.

highbugsdb.js:16

slotFree is never called before creating a booking

createBooking in db.js does not call slotFree, so double-bookings are never prevented regardless of the slot-checking logic.

Fix: Call slotFree (after fixing it) inside createBooking before inserting, and return an error if the slot is taken.

highqualityserver.js:16

No input validation on any endpoint

server.js and db.js accept all fields from req.body/req.query without type, length, or format checks. Malformed data will either cause SQL errors or be stored as garbage.

Fix: Add a validation layer (e.g., zod, joi, or express-validator) for all incoming request fields.

highsecurityauth.js:5

Hardcoded admin credentials

auth.js:5 checks username === 'admin' && password === 'admin123'. These are trivially guessable default credentials with no rate limiting or lockout.

Fix: Store hashed passwords (bcrypt/argon2) in the database. Add rate limiting and account lockout.

highsecurityauth.js:11

Cryptographically weak session token

auth.js:11 generates session tokens using Math.random() which is not cryptographically secure, making tokens predictable/guessable.

Fix: Use crypto.randomBytes(32).toString('hex') for session tokens.

highsecuritypublic/app.js:5

XSS via innerHTML with unsanitized server data

public/app.js:5-6 sets innerHTML using b.customer_name, b.service, and b.slot directly from the API response. A stored XSS payload in any booking field will execute in the admin's browser.

Fix: Use textContent or a sanitization library (DOMPurify). Never assign untrusted data to innerHTML.

highsecuritypublic/app.js:10

XSS via URL parameter in greet()

public/app.js:10 reads the 'staff' query parameter and assigns it to innerHTML without any sanitization, allowing reflected XSS via a crafted URL.

Fix: Use textContent instead of innerHTML, or sanitize the value before insertion.

highsecurityserver.js:17

No authentication on /api/booking GET endpoint

server.js:17 exposes booking details to any caller with a booking ID. There is no token check, so any unauthenticated user who guesses or enumerates an ID can read customer PII.

Fix: Require authentication or at minimum a signed token tied to the specific booking.

highsecurityserver.js:18

No authentication on POST /api/booking

server.js:18 allows anyone to create bookings without authentication, enabling spam/abuse.

Fix: Add rate limiting and, for admin-created bookings, require authentication.

mediumbugsauth.js:5

login() return value not used — token never sent to client

auth.js returns { ok: true, token: ... } but there is no middleware that stores or validates this token on subsequent requests. The token is issued but never checked anywhere.

Fix: Implement token validation middleware and apply it to protected routes.

mediumbugsdb.js:3

No database schema / migration — app will crash on first run

db.js opens bookwell.db but never creates the bookings table. On a fresh install all queries will throw 'no such table: bookings'.

Fix: Add a schema initialization block (db.exec('CREATE TABLE IF NOT EXISTS bookings (...)')) or a migration tool.

mediumbugsserver.js:16

No error handling on any route — unhandled exceptions crash the server

server.js routes call db/auth functions with no try/catch. A bad query or missing field will throw an unhandled exception, crashing the Express process.

Fix: Wrap route handlers in try/catch and return appropriate HTTP error responses.

mediumcompletenessconfig.js:4

TODO comment acknowledges secrets need moving before go-live

config.js:4 has '// TODO: move these before go-live' but the app appears to be using live keys already (sk_live_ prefix on Stripe key), indicating this was never done.

Fix: Treat this as a critical security issue, not a completeness note. Rotate all secrets immediately.

mediumcompletenessserver.js

No logout endpoint

There is a login endpoint but no logout. Sessions (such as they are) cannot be invalidated.

Fix: Implement a POST /api/logout endpoint that invalidates the session token.

mediumcompletenessdb.js

No database schema definition anywhere in the codebase

The bookings table is referenced throughout but never defined. There is no migration file, no CREATE TABLE statement, and no seed data.

Fix: Add a schema.sql or inline initialization in db.js.

mediumperformancebookings.js:10

slotFree loads entire bookings table on every availability check

bookings.js:10 runs SELECT slot FROM bookings with no WHERE clause, loading all historical bookings into memory to check a single slot.

Fix: Query for the specific slot: SELECT 1 FROM bookings WHERE slot = ? LIMIT 1

mediumqualityserver.js:16

No HTTP status codes set — all errors return 200

All route handlers call res.json() without setting a status code. Errors, not-found results, and auth failures all return HTTP 200, making client-side error handling impossible.

Fix: Use res.status(404).json(...), res.status(401).json(...), etc. as appropriate.

mediumqualityserver.js:16

No rate limiting on login endpoint

POST /api/login has no rate limiting, allowing unlimited brute-force attempts against the hardcoded credentials.

Fix: Add express-rate-limit or similar middleware to /api/login.

mediumsecurityconfig.js:6

generic.secrets.security.detected-stripe-api-key.detected-stripe-api-key

Stripe API Key detected

Fix: Stripe API Key detected

mediumsecurityserver.js:6

javascript.express.security.audit.express-check-csurf-middleware-usage.express-check-csurf-middleware-usage

A CSRF middleware was not detected in your express application. Ensure you are either using one such as `csurf` or `csrf` (see rule references) and/or you are properly doing CSRF validation in your routes with a token or cookies.

Fix: A CSRF middleware was not detected in your express application. Ensure you are either using one such as `csurf` or `csrf` (see rule references) and/or you are properly doing CSRF validation in your routes with a token or cookies.

mediumsecurityserver.js:18

No CSRF protection

State-changing POST endpoints have no CSRF token requirement. Combined with the overly permissive CORS policy this increases CSRF risk.

Fix: Add csurf middleware or use SameSite cookie attributes and verify Origin headers.

mediumsecurityserver.js

No security headers (helmet not used)

The app sets no Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, or other security headers, leaving the admin console vulnerable to clickjacking and MIME sniffing.

Fix: Add the helmet middleware: app.use(require('helmet')())

lowcompletenessconfig.js:4

Unfinished marker (TODO/FIXME/HACK)

// TODO: move these before go-live

Fix: Finish or remove this before launch.

lowcompletenessconfig.js:6

Stripe integration referenced but never used

config.js stores a Stripe secret key but no Stripe SDK is in package.json and no payment code exists. The key is exposed for no current benefit.

Fix: Remove the key until payment functionality is actually implemented; load from env when needed.

lowcompletenessconfig.js:8

SendGrid key present but no email sending code

config.js:8 has a SendGrid API key but there is no email-sending code anywhere in the codebase.

Fix: Remove until implemented; load from environment variable when needed.

lowcompletenessserver.js

No update or delete booking endpoints

The API only supports create and read. There is no way to cancel, reschedule, or delete a booking through the API.

Fix: Implement PUT /api/booking/:id and DELETE /api/booking/:id as needed.

lowqualityserver.js:25

console.log debug output left in production server startup

server.js:25 logs the port on startup via console.log. While minor, it confirms the deterministic signal about debug output.

Fix: Use a structured logger (pino, winston) with log levels rather than bare console.log.

lowqualityauth.js:14

newSessionToken exported but should be private

auth.js:14 exports newSessionToken unnecessarily, exposing an internal implementation detail.

Fix: Remove newSessionToken from exports; it is an internal helper.

lowqualitypackage.json

No dependency on a JWT library despite jwtSecret in config

package.json has no jsonwebtoken or jose dependency, yet config.js defines a jwtSecret. Either JWT is not implemented or the dependency is missing.

Fix: Either implement JWT auth with a proper library or remove the unused secret.

infocompletenessserver.js:25

Debug output left in code

app.listen(config.port, () => console.log("BookWell on " + config.port));

Fix: Finish or remove this before launch.

infoqualityconfig.js:5

dbPassword in config but better-sqlite3 does not use passwords

config.js:5 defines dbPassword but better-sqlite3 does not support password-protected databases natively. This field is misleading and unused.

Fix: Remove or clarify; if SQLCipher is intended, document and implement it explicitly.

This is the diagnosis. We also do the repair.

See real before/after receipts — every named defect re-checked and closed — on the case studies.

Get your scan →

← Back to Launchr Mechanic