Engineering Audit Report — Acme Notes
Date: 2026-07-15 · Auditor: audit-software-project skill · Repository: github.com/example/acme-notes (fictional)
Scope: Local repository inspection, plus read-only GitHub checks and a lockfile dependency scan
Sample data — this is a fictional project with invented
findings, included only to demonstrate the report format. No real audit
data is published in this repository.
States are assigned against the criteria in
criteria.html (click any control tag below to see
the threshold inline). Findings marked Not Verified or Red can be revised if
you hold evidence this audit couldn't access — see
Questions requiring human confirmation.
Executive summary
Acme Notes is a small customer-facing note-taking app with paid
subscriptions. The most severe finding is a live-looking committed secret
in the Payments feature (control 3.4) — an immediate blocker. Account
management also has a Red: deleted accounts remain visible to internal
search, a privacy-adjacent gap (control 14.2). Deployment & infra and
Notes management each carry moderate, non-blocking findings. Ownership and
documentation are in good shape.
Immediate blockers
30-day improvement plan
- Payments: Rotate committed Stripe secret key and add secret scanning to CI G1
- Account management: Fix soft-delete so purged accounts stop appearing in admin search G2
- Deployment & infra: Require CI to pass before merge on the default branch G3
- Authentication: Add integration test coverage for the login/reset-password flow G4
- Notes management: Add pagination to the unbounded
/api/notes list endpoint G5
Risk context
Confirmed at intake: customer-facing SaaS handling personal data (note
content, account emails) and payments, deployed via a third-party platform,
with a small public API (no external consumers confirmed beyond the
project's own mobile client).
Findings by feature
States: Green = adequate & evidenced.
Amber = partial/inconsistent/moderate risk.
Red = missing/significant risk.
Not Verified = evidence unavailable, not a failure.
Not Applicable = doesn't apply, reason stated.
Control IDs in parentheses trace to audit-controls.md; G-tags link
to the gap register.
Payments
🔴 1 · 🟢 3 · ⚪ 1
- Red — Live-looking Stripe secret key (
sk_live_...) committed in config/settings.py:42.
control 3.4
Green requires: no live-looking secrets found in tree or reachable history.
This finding: a live-looking secret is found — always Red, always immediate blocker.
G1
- Green — Webhook handler verifies Stripe signatures with the SDK's constant-time check before processing events (control 8.3).
- Green — Payment mutation endpoints require an authenticated session and validate input via a Zod schema before use (controls 8.1, 8.3).
- Green — Payment errors are logged with context and surfaced to the user, not swallowed (control 8.5).
- Not Verified — Whether production Stripe webhook secrets are stored separately from example config could not be confirmed from the repo alone (control 6.2).
Account management
🔴 1 · 🟢 2
- Red — Deleted accounts remain visible via
src/admin/search.ts:88, which queries the users table with no deletedAt IS NULL filter; the product's privacy policy promises deletion.
control 14.2
Green requires: hard delete, or soft-delete with a working purge job.
This finding: "deleted" user PII remains queryable through the application after a user-facing deletion action.
G2
- Green — Account deletion is initiated through a documented, reviewed endpoint, not an undocumented manual process (control 7.1).
- Green — No real-looking personal data found in seed/fixture files; all fixtures use clearly synthetic emails (control 6.3).
Authentication
🟠 1 · 🟢 3
- Amber — Login and password-reset flows are covered only by unit tests that mock the database layer; no integration test exercises the real flow end to end.
control 5.4
Green requires: at least one critical flow has integration/e2e-level coverage.
This finding: critical flows have unit coverage only, no integration/e2e layer exists.
G4
- Green — Session tokens expire after 24 hours and are verified with a real signature check, not a stub (control 8.1).
- Green — Password reset tokens are single-use and expire after 15 minutes.
- Green — TypeScript strict mode is enabled and applied to the auth module (control 4.7).
Notes management
🔴 1 · 🟠 2 · 🟢 2
- Red —
/api/notes list endpoint calls Note.findAll() with no pagination, limit, or bound (src/routes/notes.ts:21).
control 11.1
Green requires: user-facing list endpoints are paginated or otherwise bounded.
This finding: a user-facing endpoint returns an unbounded result set from a table that can grow without limit.
G5
- Amber — Note tags are fetched in a per-note loop (
src/routes/notes.ts:35-41) instead of a batched query.
G6
- Amber — No caching on the notes-search endpoint despite a config comment noting the timeout was raised from 5s to 15s to accommodate slow searches.
G8
- Green — Note content is escaped via
textContent on render; no dangerouslySetInnerHTML usage found (control 8.4).
- Green — Note create/update endpoints validate input through a shared Zod schema (control 8.3).
Deployment & infra
🔴 2 · 🟠 1 · 🟢 4 · ⚪ 2
- Red — Default branch has PR-review protection enabled but no required status checks; CI exists (
.github/workflows/test.yml) but is not required to pass before merge.
control 3.3
Green requires: CI is required and covers build/test at minimum.
This finding: CI exists but is not required.
G3
- Red — Project owner confirmed at intake that Sentry is installed but no alert rules are configured; errors are captured but no one is paged.
G7
- Amber — No OpenAPI spec or published client for the
/api/v1/ surface consumed by the project's own mobile client.
G9
- Green — Deployment is fully scripted via GitHub Actions to a managed platform; no manual SSH steps found (control 7.1).
- Green — Database migrations are version-controlled and go through the same PR review as application code (control 8.2).
- Green — Lockfile-only dependency scan (
npm audit --package-lock-only) found no high/critical vulnerabilities (control 6.1).
- Green — README covers local setup end to end; a newcomer could get the project running from it alone (control 10.1).
- Not Verified — Backup existence and restore testing could not be confirmed from the repo; flagged as a question (control 9.2).
- Not Verified — Rollback path could not be confirmed to actually work from repo evidence alone (control 7.2).
Controls not reached this pass: 2.2, 10.2 — deprioritized in favor of
higher-risk areas within the audit's time budget; recommend covering on the
next re-audit.
Questions requiring human confirmation
- Is there a tested backup/restore process for the primary database? — Deployment & infra
- Has the rollback path (redeploying a previous release) actually been exercised, not just theoretically available? — Deployment & infra
- Are Stripe webhook secrets stored separately from local/example config in the production environment? — Payments
- Does anything outside the project's own mobile client consume the public API? — Deployment & infra
Inspection boundary
What was checked: full local repository, git history, tracked dependency
lockfiles, GitHub branch protection and PR review history via gh.
What was not accessible: production infrastructure, hosting-platform
dashboards, backup/restore evidence, alerting dashboards beyond what the
project owner confirmed verbally.
GitHub read-only checks: ran · Dependency scan: ran
(npm audit --package-lock-only)
Accessibility and internationalization were not assessed; they require
browser-based testing outside this audit's static-analysis scope.
↑ Back to top · Overview · Gap register