Background
The subject of this assessment is a custom-built web application: an internal CRM and quoting tool developed for a small service-industry business. The application was built by a technically capable non-specialist using an AI coding assistant, deployed to a cloud hosting platform, and used daily by the business owner and their team to manage customer quotes, job records, and crew assignments.
The application handled sensitive operational data including proprietary pricing models, customer information, and employee details. It had been in production for several months before any security review was conducted.
The assessment was scoped to the web application layer: authentication, authorisation, API security, HTTP headers, and third-party integrations. No social engineering, network-layer, or physical access testing was performed.
What was tested
| Test area | Result |
|---|---|
| Outdated library with known CVE | Vulnerable |
| Login endpoint brute-force / rate limiting | Vulnerable |
| Client-side authentication bypass (JS disabled) | Vulnerable |
| OAuth CSRF (missing state parameter) | Vulnerable |
| Direct API access without authentication | Protected |
| SQL injection (login fields) | Not vulnerable |
| HTTP request smuggling | Not vulnerable |
| JWT algorithm confusion | Not vulnerable |
| Username enumeration via error messages | Not vulnerable |
| Username enumeration via timing | Not vulnerable |
| OAuth redirect_uri manipulation | Not vulnerable |
| Oversized payload denial-of-service | Not vulnerable |
| PII in public JavaScript source | Vulnerable |
| Security response headers | Several missing |
| CORS policy | Overly permissive |
| Session token flags | HttpOnly missing |
Executive summary
Critical finding: complete account takeover chain
The combination of an outdated third-party library (Finding 1) and an improperly flagged session token (Finding 6) created an end-to-end account takeover path: an attacker could upload a crafted file through a standard application feature, have an authenticated user open it, silently steal their session token, and gain full access to the CRM as that user — with no alerts, no lockouts, and no trace.
The application demonstrated solid fundamentals in several areas: all data API endpoints correctly returned 401 errors to unauthenticated requests, no SQL injection vulnerabilities were found, JWT signature verification was properly implemented, and no username enumeration was possible via timing or error message analysis.
However, 9 distinct vulnerabilities were identified across authentication, session management, third-party dependencies, business logic exposure, and deployment configuration — patterns entirely consistent with AI-assisted development that prioritises functional correctness over security posture.
Findings
Outdated library with known remote code execution vulnerability
A third-party JavaScript library loaded from a CDN was three major versions behind the current release. The installed version had a publicly documented, actively exploited vulnerability (CVSS 8.8 — High) allowing arbitrary JavaScript execution when processing user-uploaded files.
The application allowed users to upload files that were then rendered in-browser using this library. An attacker could craft a malicious file that, when opened by any authenticated user, would silently execute attacker-controlled JavaScript in the context of the application's domain.
Impact: Complete account takeover chain when combined with Finding 6.
No rate limiting on the login endpoint
The authentication endpoint accepted unlimited login attempts with no throttling, lockout, or CAPTCHA. Twenty sequential requests with invalid credentials all returned standard error messages with no blocking.
This allows automated brute-force and credential stuffing attacks against any known or guessed username, indefinitely. An attacker with a list of common passwords or a leaked credential database could systematically attempt access with no friction.
Impact: Unlimited automated password guessing against any user account.
Client-side only authentication guard
The application served its full HTML shell (including all business logic) to all HTTP requests regardless of authentication status. The redirect to the login page was performed exclusively by client-side JavaScript.
With JavaScript disabled — or via a direct HTTP request — the complete application interface loaded without any credentials. While API calls correctly returned 401 errors, the full HTML source, including pricing formulas, labour rates, margin calculations, overhead percentages, and application structure, was publicly readable.
Impact: Unauthenticated access to sensitive business logic embedded in HTML source.
Missing OAuth state parameter (CSRF in third-party integration)
A third-party OAuth integration flow did not include a state parameter, a basic CSRF protection required by the OAuth 2.0 specification.
Without a cryptographically random state parameter verified on callback, an attacker could trick an authenticated user into completing an OAuth authorisation flow that links the victim's account to the attacker's session — granting the attacker access to the victim's third-party data.
Impact: Attacker can gain access to victim's connected third-party account data.
Sensitive business logic exposed in publicly accessible HTML
As a direct consequence of the client-side authentication bypass (Finding 3), proprietary business configuration data was readable by anyone with a browser.
The exposed data included detailed labour rates, sales tax rates, overhead percentages, material pricing formulas, waste factors, and the complete application feature set — information a competitor, supplier, or customer could use to significant disadvantage.
Impact: Competitive intelligence and operational data exposure.
Session token accessible to JavaScript
The application's authentication cookie lacked the HttpOnly flag, meaning it could be read by any JavaScript executing on the page.
On its own this is a low-severity finding. Combined with Finding 1 (the RCE-capable file rendering library), it creates a complete account takeover chain: a malicious file triggers JavaScript execution, which reads the session token and transmits it to an attacker, who then uses it to impersonate the victim.
Impact: Enables complete account takeover chain when combined with Finding 1.
Missing security response headers
Several standard security headers were absent from HTTP responses, leaving the application without basic protections against cross-site scripting, clickjacking, and MIME-type sniffing.
The missing headers — Content-Security-Policy, X-Frame-Options, and X-Content-Type-Options — are considered baseline security hygiene and are trivially easy to add to deployment configuration. Their absence suggests they were never considered during development.
Impact: Reduced defence-in-depth against common web attack vectors.
Employee personally identifiable information in public JavaScript
Real employee full names were hardcoded in a client-side JavaScript file that was accessible without authentication.
The names were embedded in configuration objects as part of operational logic. Any visitor to the application — authenticated or not — could harvest these names. They represent a social engineering and phishing risk: an attacker who knows real employee names can craft far more convincing targeted attacks.
Impact: Employee PII exposure; social engineering attack surface.
Overly permissive CORS policy
The application set a wildcard Access-Control-Allow-Origin header on static assets, permitting any external website to make cross-origin requests.
Combined with the absent rate limiting on the login endpoint, any third-party website could script automated authentication requests from a victim's browser without their knowledge — bypassing IP-based restrictions and making attack attribution significantly harder.
Impact: Enables cross-origin scripted attacks against the login endpoint.
Recommended remediation order
Not all vulnerabilities carry equal urgency. The following priority order was provided based on exploitability, potential impact, and remediation effort:
- 1
Update the outdated library immediately
This is the most critical finding. An active, known exploit exists. Update to the patched version and set the eval mitigation flag as a temporary measure.
- 2
Add rate limiting to the login endpoint
High risk, straightforward to implement. Most cloud platforms and frameworks offer built-in rate limiting middleware.
- 3
Move the authentication guard server-side
Eliminates findings 3 and 5 in a single change. No content should be served to unauthenticated requests.
- 4
Add HttpOnly flag to the session token cookie
Breaks the account takeover chain by preventing JavaScript from reading the session token.
- 5
Move employee data to an authenticated API
Removes PII from public JavaScript and is architecturally sound regardless of the security benefit.
- 6
Add security response headers
Low effort, high return. Add to deployment configuration — one change covers every route.
- 7
Add the OAuth state parameter
Required by the OAuth specification. Prevents CSRF in third-party account linking flows.
- 8
Restrict the CORS policy
Tighten Access-Control-Allow-Origin to the application's own domain.
Key takeaways
This application was built by someone with genuine technical ability. The codebase was well-structured, the database queries were parameterised, JWT verification was correct, and the API authentication logic was sound. These are not the mistakes of a careless developer.
They are the mistakes of an AI coding assistant that was never asked the right security questions — and a development process that had no step for independent verification before going live. The vulnerabilities found are precisely the ones that AI-generated code is most likely to produce: missing headers, missing flags, missing rate limits, and outdated library recommendations drawn from stale training data.
The good news: every finding on this list has a straightforward fix. None required architectural changes. The total remediation effort, for a developer familiar with the codebase, was estimated at under two working days.
The cost of not finding these issues first? Potentially far greater — in data loss, regulatory exposure, customer trust, and business continuity.
