mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-10-08 09:01:11 +02:00
feat: importable scope configs + /scope-file REPL command; Rockstar & NASA templates
- /scope-file <path> (aliases /scopefile, /import-scope): import a ready scope config (hard allowlist + exclusions + guardrails) in the REPL — one step to "scope set correctly", instead of typing /inscope repeatedly. Pins the scope. - scope_pinned: once scope is set explicitly (scope-file / /inscope / capability), /target no longer re-derives the scope from the target, so an imported allowlist is not clobbered by picking a target. - examples/scopes/rockstargames.yaml and examples/scopes/nasa.yaml — ready TEMPLATES scoped to *.rockstargames.com / *.nasa.gov with conservative, bounty/VDP-safe guardrails (no destructive verbs, no mass accounts, low rate, forbidden payloads) and a clear "verify the program's current in/out-of-scope before running" banner. Both parse and enforce; subdomain enumeration happens inside the wildcard boundary. 422 tests passing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
4ed0226a79
commit
4ef1c9cada
3 files changed
+176
-4
No files matched your search
@@ -0,0 +1,63 @@
|
|||||||
|
# ===========================================================================
|
||||||
|
# NeuroSploit scope config — NASA (TEMPLATE)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# ⚠ BEFORE YOU RUN: confirm this matches NASA's CURRENT VDP scope.
|
||||||
|
# NASA Vulnerability Disclosure Policy: https://www.nasa.gov/nasa-vulnerability-disclosure-policy/
|
||||||
|
# (coordinated via https://bugcrowd.com/nasa-vdp). Open the policy/program
|
||||||
|
# page and align `hard` / `exclude` with the EXACT in- and out-of-scope assets
|
||||||
|
# it lists today. A VDP is for good-faith disclosure — follow its rules.
|
||||||
|
#
|
||||||
|
# `*.nasa.gov` authorizes the apex AND every subdomain, so NeuroSploit's recon
|
||||||
|
# will enumerate subdomains and test within this boundary. NASA runs MANY
|
||||||
|
# subdomains/mission sites; several are explicitly out of scope and some are
|
||||||
|
# third-party hosted — verify before testing.
|
||||||
|
#
|
||||||
|
# Import it:
|
||||||
|
# neurosploit run "*.nasa.gov" --scope-file examples/scopes/nasa.yaml --subscription
|
||||||
|
# or in the REPL:
|
||||||
|
# /scope-file examples/scopes/nasa.yaml
|
||||||
|
# /authorization https://www.nasa.gov/nasa-vulnerability-disclosure-policy/
|
||||||
|
# /target *.nasa.gov
|
||||||
|
# /run
|
||||||
|
# ===========================================================================
|
||||||
|
|
||||||
|
# --- HARD: the allowlist. Only these are testable. ------------------------
|
||||||
|
hard:
|
||||||
|
- "*.nasa.gov" # apex + every subdomain (VERIFY against the VDP)
|
||||||
|
- nasa.gov
|
||||||
|
|
||||||
|
# --- EXCLUDE: carve-outs that always beat the allowlist. ------------------
|
||||||
|
# Fill from the VDP's OUT-OF-SCOPE list. Typical for a large gov org: auth/SSO
|
||||||
|
# providers, third-party-hosted services, APIs with their own terms, and any
|
||||||
|
# system the policy names as excluded. Examples are PLACEHOLDERS — verify.
|
||||||
|
exclude:
|
||||||
|
# - auth.launchpad.nasa.gov
|
||||||
|
# - "*.ndc.nasa.gov"
|
||||||
|
# - api.nasa.gov # has its own API terms / key system — check first
|
||||||
|
|
||||||
|
# --- SOFT: guardrails inside the boundary (VDP-safe, conservative) ---------
|
||||||
|
soft:
|
||||||
|
observe_only: []
|
||||||
|
|
||||||
|
# No state-mutating verbs, no account creation — a government VDP expects
|
||||||
|
# minimal-impact, good-faith testing.
|
||||||
|
allow_destructive_methods: false
|
||||||
|
allow_account_creation: false
|
||||||
|
max_accounts: 0
|
||||||
|
|
||||||
|
# Low rate: these are production government systems.
|
||||||
|
max_requests_per_minute: 60
|
||||||
|
|
||||||
|
forbidden_payloads:
|
||||||
|
- "drop table"
|
||||||
|
- "truncate table"
|
||||||
|
- "delete from"
|
||||||
|
- "rm -rf /"
|
||||||
|
- "shutdown"
|
||||||
|
- "while(true)"
|
||||||
|
|
||||||
|
notes:
|
||||||
|
- "Authorized under NASA's Vulnerability Disclosure Policy (good-faith research only)."
|
||||||
|
- "No DoS, no social engineering, no physical testing, no disruption of operations or spacecraft/mission systems."
|
||||||
|
- "Access only the minimum data needed to demonstrate a vulnerability; never exfiltrate or retain PII/ITAR/sensitive data; stop and report if you encounter it."
|
||||||
|
- "Verify in/out-of-scope on the VDP page before each run — scope changes."
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# ===========================================================================
|
||||||
|
# NeuroSploit scope config — Rockstar Games (TEMPLATE)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# ⚠ BEFORE YOU RUN: confirm this matches the program's CURRENT scope.
|
||||||
|
# Rockstar Games bug bounty: https://hackerone.com/rockstargames
|
||||||
|
# Open the program page and align the `hard` allowlist and `exclude` list
|
||||||
|
# below with the EXACT in-scope / out-of-scope assets it lists today. Scope
|
||||||
|
# on a bounty program changes; this file is a starting point, not authority.
|
||||||
|
#
|
||||||
|
# HARD scope is enforced in code: a request whose host is not covered by `hard`
|
||||||
|
# (or hit by `exclude`) is REFUSED before it leaves. `*.rockstargames.com`
|
||||||
|
# authorizes the apex AND every subdomain, so NeuroSploit's recon will
|
||||||
|
# enumerate subdomains and test them within this boundary.
|
||||||
|
#
|
||||||
|
# Import it:
|
||||||
|
# neurosploit run "*.rockstargames.com" --scope-file examples/scopes/rockstargames.yaml --subscription
|
||||||
|
# or in the REPL:
|
||||||
|
# /scope-file examples/scopes/rockstargames.yaml
|
||||||
|
# /authorization https://hackerone.com/rockstargames
|
||||||
|
# /target *.rockstargames.com
|
||||||
|
# /run
|
||||||
|
# ===========================================================================
|
||||||
|
|
||||||
|
# --- HARD: the allowlist. Only these are testable. ------------------------
|
||||||
|
# Start with the apex + all subdomains the user named. ADD the specific extra
|
||||||
|
# roots the program lists (and REMOVE this wildcard if the program only allows
|
||||||
|
# named subdomains — check first).
|
||||||
|
hard:
|
||||||
|
- "*.rockstargames.com" # apex + every subdomain
|
||||||
|
- rockstargames.com # the apex itself
|
||||||
|
|
||||||
|
# --- EXCLUDE: carve-outs that always beat the allowlist. ------------------
|
||||||
|
# Fill these in from the program's OUT-OF-SCOPE list. Common exclusions on a
|
||||||
|
# gaming publisher: live game servers, payment/billing, support/helpdesk,
|
||||||
|
# status pages, third-party-hosted marketing. Examples below are PLACEHOLDERS —
|
||||||
|
# verify the real ones on the program page before relying on them.
|
||||||
|
exclude:
|
||||||
|
# - support.rockstargames.com
|
||||||
|
# - "*.status.rockstargames.com"
|
||||||
|
# - https://www.rockstargames.com/billing
|
||||||
|
|
||||||
|
# --- SOFT: guardrails inside the boundary (bounty-safe defaults) -----------
|
||||||
|
soft:
|
||||||
|
# Hosts you may LOOK at but never send payloads to.
|
||||||
|
observe_only: []
|
||||||
|
|
||||||
|
# State-mutating verbs (DELETE/PUT/PATCH) stay OFF — a scan must not change
|
||||||
|
# the target's state to "prove" a bug on someone's production.
|
||||||
|
allow_destructive_methods: false
|
||||||
|
|
||||||
|
# No account creation by default. Most programs forbid mass registration;
|
||||||
|
# flip to true only if the program allows it AND keep it to a couple accounts.
|
||||||
|
allow_account_creation: false
|
||||||
|
max_accounts: 0
|
||||||
|
|
||||||
|
# Conservative rate: a bounty target is production. Raise only within the
|
||||||
|
# program's stated limit.
|
||||||
|
max_requests_per_minute: 120
|
||||||
|
|
||||||
|
# Classes that damage production rather than demonstrate a bug — never run.
|
||||||
|
forbidden_payloads:
|
||||||
|
- "drop table"
|
||||||
|
- "truncate table"
|
||||||
|
- "delete from"
|
||||||
|
- "rm -rf /"
|
||||||
|
- "shutdown"
|
||||||
|
- "while(true)"
|
||||||
|
|
||||||
|
# Free-text context for the agents (NOT enforced — prose, not a control).
|
||||||
|
notes:
|
||||||
|
- "Authorized under the Rockstar Games bug bounty program (https://hackerone.com/rockstargames)."
|
||||||
|
- "Stay within the program's rules of engagement: no DoS, no social engineering, no spam/mass-account creation, no disruption of live game services."
|
||||||
|
- "Prove data access with a benign canary, never pull real player PII."
|
||||||
|
- "Verify in/out-of-scope on the program page before each run — scope changes."
|
||||||
@@ -150,7 +150,7 @@ pub(crate) const ACCEPTED: &[&str] = &[
|
|||||||
"/context", "/continue", "/creds", "/diff", "/exclude", "/exit", "/expand", "/feed",
|
"/context", "/continue", "/creds", "/diff", "/exclude", "/exit", "/expand", "/feed",
|
||||||
"/finding", "/findings", "/focus", "/forget", "/full", "/go", "/goal", "/graph", "/guardrail", "/guardrails", "/help",
|
"/finding", "/findings", "/focus", "/forget", "/full", "/go", "/goal", "/graph", "/guardrail", "/guardrails", "/help",
|
||||||
"/history", "/idle", "/inscope", "/instructions", "/integration", "/integrations", "/key", "/log",
|
"/history", "/idle", "/inscope", "/instructions", "/integration", "/integrations", "/key", "/log",
|
||||||
"/authorization", "/authz", "/program", "/logs", "/mcp", "/memory", "/model", "/models", "/objective", "/objectives", "/observe",
|
"/scope-file", "/scopefile", "/import-scope", "/authorization", "/authz", "/program", "/logs", "/mcp", "/memory", "/model", "/models", "/objective", "/objectives", "/observe",
|
||||||
"/observe-only", "/offline",
|
"/observe-only", "/offline",
|
||||||
"/onboard", "/only", "/oos", "/outofscope", "/policy", "/providers", "/proxy", "/class", "/classes", "/focus-class", "/research", "/quick", "/economy", "/eco", "/q", "/quit", "/recon",
|
"/onboard", "/only", "/oos", "/outofscope", "/policy", "/providers", "/proxy", "/class", "/classes", "/focus-class", "/research", "/quick", "/economy", "/eco", "/q", "/quit", "/recon",
|
||||||
"/pause", "/repo", "/report", "/results", "/resume", "/retest", "/revalidate", "/run", "/runs",
|
"/pause", "/repo", "/report", "/results", "/resume", "/retest", "/revalidate", "/run", "/runs",
|
||||||
@@ -162,7 +162,7 @@ pub(crate) const ACCEPTED: &[&str] = &[
|
|||||||
/// All slash-commands, for Tab completion.
|
/// All slash-commands, for Tab completion.
|
||||||
const COMMANDS: &[&str] = &[
|
const COMMANDS: &[&str] = &[
|
||||||
"/help", "/onboard", "/show", "/config", "/providers", "/model", "/key", "/sub", "/target",
|
"/help", "/onboard", "/show", "/config", "/providers", "/model", "/key", "/sub", "/target",
|
||||||
"/authorization", "/class", "/repo", "/auth", "/creds", "/focus", "/objective", "/scope-out", "/attach", "/context", "/mcp", "/offline",
|
"/scope-file", "/authorization", "/class", "/repo", "/auth", "/creds", "/focus", "/objective", "/scope-out", "/attach", "/context", "/mcp", "/offline",
|
||||||
"/class", "/research", "/quick", "/economy", "/eco", "/votes", "/chain", "/recon", "/tempmail", "/timeout", "/proxy", "/burp", "/ua", "/agents", "/only", "/theme", "/clear", "/run", "/stop", "/pause", "/continue", "/runs", "/results", "/report",
|
"/class", "/research", "/quick", "/economy", "/eco", "/votes", "/chain", "/recon", "/tempmail", "/timeout", "/proxy", "/burp", "/ua", "/agents", "/only", "/theme", "/clear", "/run", "/stop", "/pause", "/continue", "/runs", "/results", "/report",
|
||||||
"/status", "/logs", "/diff", "/retest", "/validate", "/finding", "/expand", "/integrations",
|
"/status", "/logs", "/diff", "/retest", "/validate", "/finding", "/expand", "/integrations",
|
||||||
"/memory", "/forget", "/graph", "/inscope", "/observe", "/guardrail", "/policy",
|
"/memory", "/forget", "/graph", "/inscope", "/observe", "/guardrail", "/policy",
|
||||||
@@ -289,6 +289,10 @@ struct Session {
|
|||||||
/// Engagement objective / rules-of-engagement context (why + what matters).
|
/// Engagement objective / rules-of-engagement context (why + what matters).
|
||||||
objective: Option<String>,
|
objective: Option<String>,
|
||||||
authorization: Option<String>,
|
authorization: Option<String>,
|
||||||
|
/// Set once the operator defines scope explicitly (scope-file, /inscope, or
|
||||||
|
/// a capability). While pinned, `/target` stops re-deriving the scope from
|
||||||
|
/// the target, so an imported allowlist is not clobbered by picking a target.
|
||||||
|
scope_pinned: bool,
|
||||||
/// Explicit out-of-scope exclusions the agents must not touch.
|
/// Explicit out-of-scope exclusions the agents must not touch.
|
||||||
out_of_scope: Option<String>,
|
out_of_scope: Option<String>,
|
||||||
/// Authorization boundary + guardrails, enforced by the harness.
|
/// Authorization boundary + guardrails, enforced by the harness.
|
||||||
@@ -336,6 +340,7 @@ impl Default for Session {
|
|||||||
instructions: None,
|
instructions: None,
|
||||||
objective: None,
|
objective: None,
|
||||||
authorization: None,
|
authorization: None,
|
||||||
|
scope_pinned: false,
|
||||||
out_of_scope: None,
|
out_of_scope: None,
|
||||||
policy: Default::default(),
|
policy: Default::default(),
|
||||||
capability: None,
|
capability: None,
|
||||||
@@ -720,7 +725,11 @@ pub async fn repl(base: &Path, auth: SessionAuth) -> anyhow::Result<()> {
|
|||||||
// no capability, the target the operator picks IS the grant
|
// no capability, the target the operator picks IS the grant
|
||||||
// (same model as `neurosploit run <url>`); explicit excludes
|
// (same model as `neurosploit run <url>`); explicit excludes
|
||||||
// and guardrails are preserved.
|
// and guardrails are preserved.
|
||||||
if s.capability.is_none() {
|
// Only re-derive when scope was NOT set explicitly (no
|
||||||
|
// imported scope-file, no /inscope, no capability) — otherwise
|
||||||
|
// picking a target would clobber the operator's allowlist.
|
||||||
|
let rederive = s.capability.is_none() && !s.scope_pinned;
|
||||||
|
if rederive {
|
||||||
let keep_exclude = s.policy.exclude.clone();
|
let keep_exclude = s.policy.exclude.clone();
|
||||||
let keep_soft = s.policy.soft.clone();
|
let keep_soft = s.policy.soft.clone();
|
||||||
let mut np = harness::scope::ScopePolicy::for_target(&seeds[0]);
|
let mut np = harness::scope::ScopePolicy::for_target(&seeds[0]);
|
||||||
@@ -736,7 +745,9 @@ pub async fn repl(base: &Path, auth: SessionAuth) -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
if ts.len() > 1 { println!(" targets ({}): {}", ts.len(), seeds.join(", ")); println!(" \x1b[2m/run tests them sequentially, one report each\x1b[0m"); }
|
if ts.len() > 1 { println!(" targets ({}): {}", ts.len(), seeds.join(", ")); println!(" \x1b[2m/run tests them sequentially, one report each\x1b[0m"); }
|
||||||
else { println!(" target: {}", seeds.first().cloned().unwrap_or_default()); }
|
else { println!(" target: {}", seeds.first().cloned().unwrap_or_default()); }
|
||||||
if let Some(d) = &wildcard_domain {
|
if s.scope_pinned {
|
||||||
|
println!(" \x1b[2mscope: using the imported scope ({}) — target must fall inside it\x1b[0m", s.policy.summary());
|
||||||
|
} else if let Some(d) = &wildcard_domain {
|
||||||
println!(" \x1b[2mscope: *.{d} — apex + all subdomains authorized; recon will enumerate subdomains\x1b[0m");
|
println!(" \x1b[2mscope: *.{d} — apex + all subdomains authorized; recon will enumerate subdomains\x1b[0m");
|
||||||
// Nudge recon toward active subdomain discovery for a domain-wide engagement.
|
// Nudge recon toward active subdomain discovery for a domain-wide engagement.
|
||||||
if s.recon_intensity < 3 { s.recon_intensity = 3; }
|
if s.recon_intensity < 3 { s.recon_intensity = 3; }
|
||||||
@@ -832,6 +843,28 @@ pub async fn repl(base: &Path, auth: SessionAuth) -> anyhow::Result<()> {
|
|||||||
s.objective = Some(arg.to_string());
|
s.objective = Some(arg.to_string());
|
||||||
println!(" objective set — steers what agents prioritise and what counts as impact");
|
println!(" objective set — steers what agents prioritise and what counts as impact");
|
||||||
}
|
}
|
||||||
|
"/scope-file" | "/scopefile" | "/import-scope" => {
|
||||||
|
let path = arg.trim().trim_start_matches('@');
|
||||||
|
if path.is_empty() {
|
||||||
|
println!(" import a scope config (hard allowlist + exclusions + guardrails):");
|
||||||
|
println!(" /scope-file examples/scopes/rockstargames.yaml");
|
||||||
|
println!(" current scope: {}", s.policy.summary());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match harness::scope::ScopePolicy::from_file(std::path::Path::new(path)) {
|
||||||
|
Ok(sp) => {
|
||||||
|
if sp.hard.is_empty() {
|
||||||
|
println!(" \x1b[33m⚠ {path} sets no hard scope — nothing would be authorized; not applied.\x1b[0m");
|
||||||
|
} else {
|
||||||
|
s.scope_pinned = true;
|
||||||
|
s.policy = sp;
|
||||||
|
println!(" \x1b[32m📋 scope imported\x1b[0m from {path} — {}", s.policy.summary());
|
||||||
|
println!(" \x1b[2m/target a host inside this scope, then /run. Add /authorization <program-url> to record the authorization.\x1b[0m");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => println!(" \x1b[31m⛔ could not read {path}: {e}\x1b[0m"),
|
||||||
|
}
|
||||||
|
}
|
||||||
"/authorization" | "/authz" | "/program" => {
|
"/authorization" | "/authz" | "/program" => {
|
||||||
if arg == "clear" { s.authorization = None; println!(" authorization reference cleared"); continue; }
|
if arg == "clear" { s.authorization = None; println!(" authorization reference cleared"); continue; }
|
||||||
if arg.is_empty() {
|
if arg.is_empty() {
|
||||||
@@ -1263,6 +1296,7 @@ pub async fn repl(base: &Path, auth: SessionAuth) -> anyhow::Result<()> {
|
|||||||
// dropped it is the same lie the ceiling exists to prevent.
|
// dropped it is the same lie the ceiling exists to prevent.
|
||||||
let before = s.policy.hard.len();
|
let before = s.policy.hard.len();
|
||||||
s.policy.allow(arg);
|
s.policy.allow(arg);
|
||||||
|
s.scope_pinned = true;
|
||||||
let refused = reapply_grant(&mut s);
|
let refused = reapply_grant(&mut s);
|
||||||
if !refused.is_empty() {
|
if !refused.is_empty() {
|
||||||
println!(" \x1b[33m⛔ outside the capability grant, not authorized:\x1b[0m {}", refused.join(", "));
|
println!(" \x1b[33m⛔ outside the capability grant, not authorized:\x1b[0m {}", refused.join(", "));
|
||||||
@@ -2387,6 +2421,7 @@ fn help() {
|
|||||||
h("/chain <n>", "attack-chain depth (post-exploitation pivots; 0 = off)");
|
h("/chain <n>", "attack-chain depth (post-exploitation pivots; 0 = off)");
|
||||||
h("/recon <1-4>", "recon intensity: 1 quick · 2 standard · 3 deep · 4 exhaustive (installs tools)");
|
h("/recon <1-4>", "recon intensity: 1 quick · 2 standard · 3 deep · 4 exhaustive (installs tools)");
|
||||||
h("/class <a,b>", "focus a run on vuln classes (idor,sqli,xss,ssrf,…) — pins the matching agents");
|
h("/class <a,b>", "focus a run on vuln classes (idor,sqli,xss,ssrf,…) — pins the matching agents");
|
||||||
|
h("/scope-file <path>", "import a ready scope config (hard allowlist + exclusions + guardrails) — e.g. examples/scopes/rockstargames.yaml");
|
||||||
h("/authorization <url>", "declare the program/authorization (e.g. a bug-bounty URL) — recorded; does NOT widen scope");
|
h("/authorization <url>", "declare the program/authorization (e.g. a bug-bounty URL) — recorded; does NOT widen scope");
|
||||||
h("/research", "whitebox/greybox: hunt a NOVEL, CVE-reportable bug (known-CVE dedup + patch-diff variant analysis)");
|
h("/research", "whitebox/greybox: hunt a NOVEL, CVE-reportable bug (known-CVE dedup + patch-diff variant analysis)");
|
||||||
h("/quick", "economy preset: short, low-cost run (1 voter · 1 chain round · light recon · ≤6 agents)");
|
h("/quick", "economy preset: short, low-cost run (1 voter · 1 chain round · light recon · ≤6 agents)");
|
||||||
|
|||||||
Reference in new issue
Block a user