WEBISOFT®
Confidential
Engineering Assessment
Report v1.0
Vibe Check® · Sample Report
AI-Built Software Security Audit
A senior human engineer’s independent review of an AI-built web application. Every finding read, verified, and signed by hand.
- Client
- ████████ Inc., Series-A B2B SaaS
- Application
- Production web app, ~4,200 lines, built with an AI app builder
- Engagement window
- 24-hour turnaround
- Access
- Read-only source repository
- Prepared by
- Webisoft · Vibe Check®
- Reviewing engineer
- William Marchand, Senior Engineer
Real report format. Client details and findings redacted for confidentiality.
SAMPLE
01 · Executive OverviewVibe Check®
Section 1
Executive overview
1.1 Introduction
████████ Inc. (“the Client”), a Series-A B2B SaaS company, engaged Webisoft to perform a Vibe Check: an independent, human-led security and quality audit of a production web application their team assembled largely with an AI app builder. The application is roughly 4,200 lines of code and handles account sign-up, a customer dashboard, and card payments.
The review was delivered on the standard Vibe Check basis: a fixed fee of $250, read-only access to the source repository, and a signed report inside 24 hours. No live customer data, admin credentials, or passwords were requested or accessed at any point.
1.2 Audit summary
One senior engineer reviewed the full codebase by hand, supported by Webisoft’s automated first pass. The purpose of the audit was to answer, in plain English, three questions the Client’s customers and investors are starting to ask: is anything exposed, can one customer reach another’s data, and will the app hold up as it grows.
In summary, the engineer identified 3 Critical issues that warrant action before the next enterprise conversation, 5 Warnings worth scheduling into the coming sprints, and 12 checks that passed. The three Critical findings are common to AI-built applications: a live payment key shipped inside the app, an interface that returns any customer’s records when an ID is changed, and a password-reset flow that can be guessed. None require a rewrite; each has a concrete, costed fix in Section 6.
01 · Executive OverviewMethodology
1.3 Test approach & methodology
A Vibe Check pairs an automated first pass with a full manual review, so that coverage is broad and every reported finding is verified by a human before it reaches the Client. The following phases were used:
- Automated first pass: secret scanning, dependency and CVE checks, and static analysis across the repository.
- Manual code review of authentication, access control, and payment logic, line by line.
- Data-flow tracing to confirm whether one account can reach another account’s data.
- Configuration review: CORS, error handling, environment and build output.
- Scalability read: query patterns, pagination, and behaviour under concurrent load.
- Human verification of every finding, with false positives discarded before delivery.
Risk methodology (Likelihood × Impact)
Each finding is rated by measuring the Likelihood that it will be exploited or hit in practice, and the Impact should that happen. Each axis runs 1 to 5. The two values combine into a severity band that maps to the Vibe Check scale used throughout this report.
| Band | Combined score | What it means for you |
| Critical | 8–10 | Fix before your next enterprise deal, security review, or launch. Real risk of exposure, data loss, or account takeover today. |
| Warning | 4–7 | Schedule into the coming sprints. Not on fire, but it will be exploited or will bite you as you grow. |
| Pass | 1–3 | Reviewed and healthy, or low enough to note and move on. |
Likelihood × Impact matrix
Placement of this engagement’s eight findings. Rows are Impact (5 at top), columns are Likelihood (5 at right).
| Impact ↓ / Likelihood → | 1 | 2 | 3 | 4 | 5 |
| 5 | | | VC-02 | VC-01 | VC-03 |
| 4 | | | VC-04 | VC-05 | |
| 3 | | | VC-07 | VC-06 | |
| 2 | | | VC-08 | | |
| 1 | | | | | |
01 · Executive OverviewScope
1.4 Assessment scope
The assessment was scoped to the source code of the Client’s production web application, provided through a read-only repository invitation.
- Repository: ████████████ (read-only, single reviewer)
- Application: single-page front end, Node.js API, and a hosted database, generated and iterated with an AI app builder.
- Size: ~4,200 lines across front end, API, and configuration.
- In scope: application source, authentication and access control, payment integration, dependency manifest, build output, and deployment configuration.
- Out of scope: live customer data, third-party infrastructure internals, physical security, and social-engineering testing.
Access was limited to the single reviewing engineer and revoked on delivery of this report.
02 · Assessment SummaryFindings Overview
Section 2
Assessment summary & findings overview
61/100
Overall risk score
Security38
Data privacy52
Payments71
Scalability64
Higher is healthier. The overall score is held down by the three Critical findings; each is individually fixable without a rewrite.
Findings overview
| ID | Finding | Severity | Status |
| VC-01 | Live payment key shipped in the app bundle | Critical | Open · fix ready |
| VC-02 | One customer can read another customer’s data (IDOR) | Critical | Open · fix ready |
| VC-03 | Password reset can be guessed (account takeover) | Critical | Open · fix ready |
| VC-04 | Database queries built from raw user input | Warning | Open |
| VC-05 | No limit on login attempts | Warning | Open |
| VC-06 | API accepts requests from any website (CORS) | Warning | Open |
| VC-07 | Abandoned dependency with a known CVE | Warning | Open |
| VC-08 | Error pages leak internal details | Warning | Open |
Status reflects a first-delivery report. On a live engagement, this column is updated as the Client remediates: Solved, Partially solved, Risk accepted, or Acknowledged.
03 · FindingsTechnical Details
Section 3
Findings & technical details
Every finding leads with what it means for your business, then the technical detail, redacted evidence, the fix, and the rough effort involved. Your $250 audit fee is credited against any of it.
VC-01 · Secrets management
A live payment key is shipped inside your app.
Critical
What this means for you
Your live payment secret is sitting in code that anyone who visits your site can download and read. With it, someone can issue refunds, read your payment history, and move money as if they were you. This is the single most urgent item in the report, and rotating the key takes minutes.
Technical detail
The live Stripe secret key is hard-coded into the front-end bundle and shipped to every browser. AI app builders frequently place server-only secrets in client-visible code because the prompt did not distinguish between a publishable key and a secret key. The key is also present in committed source, so it exists in the repository history even if removed from the current file.
Evidence (redacted)
/src/config.js · line 218 · bundled and served to the browser
// loaded client-side, visible in every browser
const STRIPE_SECRET = "sk-live-████████████████████";
const stripe = require("stripe")(STRIPE_SECRET);
Risk rating
- Likelihood
- 4 / 5
- Impact
- 5 / 5
- Severity
- Critical
Recommendation
Rotate the exposed key in the Stripe dashboard immediately so the leaked value is dead. Move the secret to a server-side environment variable that is never sent to the browser, and switch the front end to the publishable key only. Purge the key from the committed history. We can pair on this the same day the audit lands.
Remediation status: Open · fix ready · recommended before any further deploys
VC-02 · Broken access control (IDOR)
One customer can read another customer’s data.
Critical
What this means for you
Any logged-in customer can see other customers’ records simply by changing a number in the address bar. For a B2B SaaS, that is a cross-customer data breach: the kind of incident that ends enterprise deals and triggers disclosure obligations. It is invisible in normal use, which is why it survives to production so often.
Technical detail
An API endpoint returns a record based on the ID in the URL without checking that the record belongs to the account making the request. Changing the ID returns someone else’s data. This is an Insecure Direct Object Reference (IDOR). AI builders reliably generate the “fetch by id” path and just as reliably omit the ownership check, because it was never asked for.
Evidence (redacted)
/api/routes/invoices.js · line 74
app.get("/api/invoices/:id", auth, async (req, res) => {
// returns the record for ANY id, no owner check
const invoice = await db.invoices.findById(req.params.id);
res.json(invoice); // GET /api/invoices/██ returns another tenant
});
Risk rating
- Likelihood
- 3 / 5
- Impact
- 5 / 5
- Severity
- Critical
Recommendation
Scope every record lookup to the authenticated account, for example by filtering on both the record ID and the owner’s account ID, and return 404 when they do not match. Apply the same ownership check across all endpoints that take an ID, not just this one. Add a test that a second account cannot read the first account’s records.
Remediation status: Open · fix ready · audit all ID-based endpoints together
VC-03 · Authentication
Your password reset can be guessed.
Critical
What this means for you
The link that lets a user reset their password can be guessed, and it never expires. That means an attacker can take over accounts, including admin accounts, without ever knowing the password. Combined with VC-02, one compromised account can read across the whole customer base.
Technical detail
The password-reset token is a short, sequential value derived from a timestamp rather than a cryptographically random secret, and it carries no expiry. An attacker can enumerate recently issued tokens and complete a reset for an account they do not own. This is a direct account-takeover path.
Evidence (redacted)
/api/auth/reset.js · line 41
// predictable token, no expiry stored
const token = Date.now().toString(36); // guessable
await db.resets.insert({ user: ████, token });
// reset accepted whenever token matches, forever
Risk rating
- Likelihood
- 5 / 5
- Impact
- 5 / 5
- Severity
- Critical
Recommendation
Generate reset tokens from a cryptographically secure random source, store only a hash of the token, expire it after a short window (15 to 60 minutes), and invalidate it on first use. Rate-limit reset requests. These are standard building blocks; the fix is well understood and low risk.
Remediation status: Open · fix ready
VC-04 · Injection (High)
Database queries are built from raw user input.
Warning
What this means for you
Search and filter inputs are dropped straight into database queries. A crafted input can read or alter data it should not, or make the query return everything. It has not been weaponized here, but it is the classic path to a data breach and should be closed before you scale.
Technical detail
User-supplied values reach the query layer without parameterization or validation, creating a NoSQL/SQL injection surface on the search and filter endpoints. The query is assembled by string concatenation from request fields.
Evidence (redacted)
/api/routes/search.js · line 52
// req.query.q flows straight into the query object
const results = await db.users.find(
{ $where: `this.name == '${req.query.q}'` } // injectable
);
Risk rating
- Likelihood
- 3 / 5
- Impact
- 4 / 5
- Severity
- Warning (High)
Recommendation
Use parameterized queries or the database driver’s typed query builders, never string interpolation. Validate and whitelist input fields, and drop the $where operator entirely. Add input-validation middleware at the API boundary.
Remediation status: Open
VC-05 · Authentication (High)
There is no limit on login attempts.
Warning
What this means for you
Anyone can try passwords against your login as fast as they like. That opens the door to automated password-guessing and credential-stuffing attacks using leaked passwords from other breaches. It also lets an attacker hammer your server, driving up cost and slowing the app for real users.
Technical detail
The login endpoint has no rate limiting, no throttling, and no account lockout or backoff. There is nothing to slow or stop high-volume automated attempts.
Evidence (redacted)
/api/auth/login.js · line 19
app.post("/api/login", async (req, res) => {
// no rate limit, no lockout, unlimited attempts
const ok = await checkPassword(req.body.email, req.body.password);
res.json({ ok });
});
Risk rating
- Likelihood
- 4 / 5
- Impact
- 4 / 5
- Severity
- Warning (High)
Recommendation
Add rate limiting per IP and per account on login and reset endpoints, with exponential backoff and a temporary lockout after repeated failures. Log repeated failures for monitoring. A small middleware handles most of this.
Remediation status: Open
VC-06 · Configuration (Medium)
Your API accepts requests from any website.
Warning
What this means for you
Your API is configured to trust every website on the internet. That makes it easier for a malicious page to act against your API using a logged-in user’s session, and it removes a layer of protection you should have by default.
Technical detail
Cross-Origin Resource Sharing (CORS) is set to a wildcard *, allowing any origin to call the API. This is a common AI-builder default meant to “make it work” during development that then ships to production.
Evidence (redacted)
/api/server.js · line 12
app.use(cors({ origin: "*" })); // any website may call the API
Risk rating
- Likelihood
- 4 / 5
- Impact
- 3 / 5
- Severity
- Warning (Medium)
Recommendation
Restrict CORS to your known front-end domains only. Pair it with proper cookie and CSRF handling for authenticated requests. This is a small, low-risk configuration change.
Remediation status: Open
VC-07 · Dependencies (Medium)
An abandoned dependency carries a known CVE.
Warning
What this means for you
Your app depends on an outside package that is no longer maintained and has a publicly known security flaw. Because the flaw is public, it is easy to find and exploit, and no fix is coming from the original author. AI tools sometimes even invent package names that attackers then register, so every dependency needs a human sanity check.
Technical detail
The dependency manifest pins an outdated, unmaintained package with a published CVE. It has had no release in over three years and open, unpatched advisories. The version is also pinned loosely, so the build can pull an unexpected release.
Evidence (redacted)
/package.json · dependencies
"dependencies": {
"████████████": "^0.2.1", // unmaintained, CVE-20██-████
"express": "^4.18.2"
}
Risk rating
- Likelihood
- 4 / 5
- Impact
- 3 / 5
- Severity
- Warning (Medium)
Recommendation
Replace the package with a maintained equivalent or remove it if unused, pin exact versions, and add automated dependency and CVE scanning to your build so this is caught continuously. Verify that every dependency name is a real, maintained project.
Remediation status: Open
VC-08 · Information disclosure (Medium)
Error pages leak internal details.
Warning
What this means for you
When something goes wrong, your app shows visitors a full technical error, including file paths, database details, and library versions. That hands an attacker a map of how your system is built and makes every other weakness easier to find and exploit.
Technical detail
The API returns raw stack traces and internal error objects to the client in production. Verbose error output exposes file paths, dependency versions, and query fragments. This is a default development behaviour that was never switched off.
Evidence (redacted)
/api/server.js · error handler · line 140
app.use((err, req, res, next) => {
// full stack trace returned to the client
res.status(500).json({ error: err.stack }); // leaks internals
});
Risk rating
- Likelihood
- 3 / 5
- Impact
- 2 / 5
- Severity
- Warning (Medium)
Recommendation
Add a global error handler that returns a generic message and a reference ID to the client, while logging the full detail server-side only. Disable verbose errors and debug mode in production.
Remediation status: Open
04 · Scalability ReadGrowth & Load
Section 4
Scalability read
What this means for you
The app runs fine for your current handful of users, but the way it talks to the database will not hold. On our read, it starts to slow badly and collapses around 1,000 concurrent users. That is a wall you will hit during a launch, a big customer onboarding, or a press moment, exactly when you can least afford it.
Technical detail
Two patterns drive this. First, list endpoints load related records one row at a time inside a loop (the “N+1 query” problem), so a single dashboard view can fire hundreds of database calls. Second, list endpoints have no pagination, so every request loads the full table into memory and returns it. Both scale linearly with data and users, and together they saturate the database connection pool under concurrent load.
Evidence (redacted)
/api/routes/dashboard.js · line 96
const accounts = await db.accounts.findAll(); // no pagination: whole table
for (const a of accounts) {
a.owner = await db.users.findById(a.ownerId); // N+1: one query per row
}
res.json(accounts);
Recommendation
Load related records in a single query (join or batched lookup) to remove the N+1 pattern, add pagination with sensible page sizes to every list endpoint, and add database indexes on the fields used for lookups and sorting. Re-test under simulated concurrent load to confirm headroom well past 1,000 users.
- Collapses around
- 1,000 concurrent users
- Scalability score
- 64 / 100
05 · What PassedHealthy Checks
Section 5
What passed
Twelve checks came back healthy. A Vibe Check reports the good with the bad, so you know what not to spend money on.
P01Passwords are hashed with a modern algorithm
P02Login sessions use signed, HTTP-only cookies
P03Traffic is served over HTTPS with valid certificates
P04Payment card data is never stored on your servers
P05Sign-up input is length- and type-checked
P06No hard-coded admin backdoor accounts
P07Front end escapes user content (no obvious XSS)
P08Database is not exposed to the public internet
P09Secrets, aside from VC-01, use environment variables
P10File uploads restrict type and size
P11Dependencies, aside from VC-07, are current
P12Logout fully clears the session
06 · Fix PlanPrioritized & Scoped
Section 6
Prioritized fix plan
In priority order. Each item shows rough effort. The fixes are written so your own AI or developer can execute the smaller ones; the deeper ones are worth an engineer.
C1
Rotate and hide the live payment key (VC-01)
Rotate the key today, move it server-side, purge it from history. Do this first.
2–4 hrs · DevOps
C2
Close the cross-customer data hole (VC-02)
Scope every record lookup to the logged-in account across all endpoints.
1–2 days · Engineer
C3
Fix the password-reset flow (VC-03)
Random, hashed, single-use, expiring tokens plus reset rate limiting.
~1 day · Engineer
W1
Parameterize database queries (VC-04)
Remove string-built queries and add input validation at the API boundary.
1–2 days · Engineer
W2
Rate-limit login and reset (VC-05)
Per-IP and per-account limits with backoff and temporary lockout.
4–6 hrs · Engineer
W3
Lock down CORS (VC-06)
Restrict to your own domains. Small config change, give it to your AI.
1–2 hrs · AI-safe
W4
Replace the abandoned dependency (VC-07)
Swap for a maintained package, pin versions, add CVE scanning to the build.
3–5 hrs · Engineer
W5
Silence verbose errors (VC-08)
Generic client errors with a reference ID, full detail logged server-side.
3–4 hrs · AI-safe
S1
Fix N+1 queries and add pagination (Scalability)
Batch related lookups, paginate list endpoints, add indexes, retest under load.
2–3 days · Engineer
Your $250 audit fee is credited toward any of these fixes. Hire Webisoft for all of the work or a single item and the audit is effectively free. There is no obligation to use us: the fix list is yours to hand to your own AI, your developer, or another firm, with the estimates above as a reference.
07 · ClosingSign-off
Section 7
Closing & sign-off
The application is in solid shape for its stage, and nothing here calls for a rewrite. The three Critical findings are the ones to act on before your next enterprise conversation or security review; each has a concrete, same-week fix. The Warnings are worth scheduling into the coming sprints, and the scalability work is best done before your next growth push rather than during it.
This kind of report is exactly what enterprise customers, insurers, and investors are starting to ask AI-built companies for. A Vibe Check is not a formal certification, but it tells you honestly where you stand and what closing the gap would take.
Every finding in this report was read and verified by hand by the engineer below. Findings, evidence, and client identity have been redacted for this public sample.
William Marchand
Senior Engineer · Webisoft
Reviewed & signed · Vibe Check® · Report v1.0