package recon

import "github.com/Armur-Ai/Pentest-Swarm-AI/internal/pipeline"

// nodegoatProfile is the surface of OWASP NodeGoat, an intentionally-vulnerable
// Express/MongoDB retirement-savings app that demonstrates the OWASP Top 10 in
// Node. It is server-rendered (form POSTs, session cookie), not a JSON API, so
// recon's crawler sees the pages but not which ones carry planted flaws — this
// profile points the exploit agent straight at them.
//
// AUTH IS COOKIE-BASED: POST /login sets a session cookie (connect.sid); capture
// it from Set-Cookie and resend it as a Cookie header on later requests (not a
// bearer token). Routes verbatim from app/routes/index.js (master).
var nodegoatProfile = apiProfile{
	name: "nodegoat",
	// /login and /signup are always-mounted HTML form routes (200) and 404 on an
	// unrelated app that passes the negative control.
	signatures: []signatureRoute{
		{method: "GET", path: "/login"},
		{method: "GET", path: "/signup"},
	},
	build: func(base string) []pipeline.EndpointRecord {
		return []pipeline.EndpointRecord{
			{
				URL:    base + "/signup",
				Method: "POST",
				Notes: "Registration (form-encoded). Fields: firstName, lastName, userName, password, verify, " +
					"email (and bank fields on profile later). Create an attacker account, then log in.",
			},
			{
				URL:    base + "/login",
				Method: "POST",
				Notes: "Login (form-encoded). Fields: userName, password. On success the response sets a " +
					"session cookie (connect.sid) — capture it from Set-Cookie and resend as " +
					"'Cookie: connect.sid=...' on the authenticated routes below. NodeGoat has no bearer token.",
			},
			{
				URL:         base + "/allocations/{{victim_userId}}",
				Method:      "GET",
				Interesting: true,
				Parameters:  []string{"userId"},
				Notes: "IDOR / BOLA (flagship): /allocations/:userId is guarded only by isLoggedIn — it never " +
					"checks that :userId is the session user. Logged in as a low-privilege user, request another " +
					"user's numeric userId (enumerate 1,2,3,…). A 200 returning another user's financial " +
					"allocations proves broken object-level authorization. Canonical --verify differential case " +
					"(attack userId vs a non-existent control id).",
			},
			{
				URL:         base + "/profile",
				Method:      "POST",
				Interesting: true,
				Notes: "STORED XSS + MASS ASSIGNMENT: profile update (form-encoded) persists fields " +
					"(firstName, lastName, ssn, dob, address, bankAcc, bankRouting) and reflects them unescaped " +
					"on the profile page — inject a script payload in a name field and confirm it executes when " +
					"the profile is viewed. Also probe for extra privileged fields accepted beyond the form.",
			},
			{
				URL:         base + "/contributions",
				Method:      "POST",
				Interesting: true,
				Notes: "SERVER-SIDE JS INJECTION (A1): the contributions handler eval()s the posted " +
					"preTax/afterTax/roth values. Submit a JS expression instead of a number (e.g. a payload " +
					"that performs arithmetic or touches the process) in those fields — evidence that the " +
					"expression evaluated server-side proves remote code execution via SSJS.",
			},
			{
				URL:         base + "/learn",
				Method:      "GET",
				Interesting: true,
				Parameters:  []string{"url"},
				Notes: "OPEN REDIRECT / SSRF SINK: GET /learn?url= redirects to req.query.url with no " +
					"validation. Point it at an attacker URL to prove open redirect; point it at an internal/OOB " +
					"host to test SSRF (the out-of-band oracle confirms a server-side fetch). This is a direct " +
					"url-parameter sink the SSRF sweep targets.",
			},
			{
				URL:         base + "/memos",
				Method:      "POST",
				Interesting: true,
				Notes: "STORED XSS (persistent): POST /memos stores a memo rendered to every user unescaped. " +
					"A script payload that executes when GET /memos is viewed proves persistent XSS.",
			},
			{
				URL:         base + "/benefits",
				Method:      "POST",
				Interesting: true,
				Notes: "MISSING FUNCTION-LEVEL AUTHZ (A7): the admin-only benefits editor is reachable by any " +
					"logged-in user (the isAdmin gate is disabled). A 200 updating benefits as a non-admin proves " +
					"broken function-level authorization.",
			},
		}
	},
	chains: nodegoatChains,
}
