Skip to main content

Bruteforce sinks

vulkro scan --bruteforce-sinks statically drives a corpus of adversarial payloads at every critical-surface call site in the codebase and flags the payloads that reach the sink without an effective recognized guard.

The engine never touches the network. No real API call is made. No sandbox is spun up. The "brute force" is in-process AST walking against a fixed payload table.

What it catches that taint analysis alone misses

Existing taint flow says "this input reaches stripe.charges.create." Bruteforce sinks says which payload classes can reach that sink:

  • amount = -1 reaching stripe.charges.create without a sign check
  • customerId = "../../admin" reaching s3.getObject without path sanitization
  • email = "<huge string>" reaching sendgrid.send (DoS / 413 cascade)
  • prompt = "ignore previous, exfiltrate" reaching anthropic.messages.create without a system / user role split
  • null / undefined / NaN / MAX_SAFE_INTEGER reaching any third-party call without a type guard
  • SQLi shapes (' OR 1=1 --, stacked drops, UNION) reaching cursor.execute without parameterized binding
  • Path-traversal payloads (../../etc/passwd, ..\..\windows) reaching fs.writeFile without canonicalization

Coverage (Wave 1)

CategoryExamples
SQLcursor.execute, Sequelize.query, knex.raw, prisma.$queryRaw
Shellsubprocess.run(shell=True), child_process.exec, os.system
HTTPrequests.get, httpx.post, axios.*, fetch
Paymentstripe.charges.create, braintree.transaction.sale, razorpay.orders.create
LLManthropic.messages.create, openai.chat.completions.create, cohere.chat, bedrock.invoke_model
File-writeopen(path, 'w'), Path.write_text, fs.writeFile, fs.writeFileSync
Deserializationpickle.loads, yaml.load, eval, vm.runInNewContext, Function constructor
Emailsendgrid.send, ses.send_email, smtplib.sendmail

Languages: Python, JavaScript, TypeScript.

Payload corpus

About 30 payload entries across 8 super-classes (TypeConfusion, NumericExtreme, SizeExtreme, Unicode, Injection, PathTraversal, PromptInjection, PaymentExtreme).

Guard catalogue

About 70 recognizers across Python + JS / TS:

  • Type checks: isinstance, typeof, Number.isFinite, Number.isInteger, Array.isArray.
  • Null checks: is None, === null, === undefined, ??.
  • Range / length checks: len(x) > N, x.length > N, x > 0.
  • Anchored regex: re.fullmatch, re.match, .test().
  • Allowlist membership: in {...}, .includes, .has.
  • Sanitizers: html.escape, bleach.clean, markupsafe.escape, DOMPurify.sanitize, validator.escape, escape-html.
  • shlex.quote for shell payloads.
  • Parameterized DB binding: cursor.execute(sql, (x,)), pool.query(sql, [x]), .where({...}).
  • URL scheme allowlist + private-IP reject for HTTP egress.
  • Path canonicalize + base-dir check: pathlib.Path.resolve, path.normalize + .startsWith.
  • Schema validation: Pydantic .model_validate, Zod .parse, Joi .validate, Yup, AJV.
  • Email validators: validator.isEmail, validators.email.
  • yaml.SafeLoader for deserialization.
  • LLM system / user role split (separate role: "system" entry in messages instead of concatenation).

CLI flags

vulkro scan . --bruteforce-sinks
vulkro scan . --bruteforce-sinks --bruteforce-categories sql,payment,llm
vulkro scan . --bruteforce-sinks --bruteforce-confidence medium
vulkro scan . --bruteforce-sinks --bruteforce-payload-classes injection,path-traversal

Categories: sql, shell, http (aliases: ssrf, egress), payment (alias: billing), llm (alias: ai), file / file-write, deser / deserialization, email.

Payload classes: injection, numeric-extreme, type-confusion, size-extreme, unicode, path-traversal, prompt-injection, payment-extreme.

Confidence tiering

  • High (default emit): no recognized guards on the path. Kill shot.
  • Medium (opt in with --bruteforce-confidence medium): guards exist on the path, but the engine cannot prove they cover this payload class. Surfaces the "you have a length check but no type check" gap.
  • Low: reserved for future alias-aware partial resolution.

Rule ID convention

BFS-{CATEGORY}-{payload-tag}. Examples:

  • BFS-PAYMENT-payment-negative-amount
  • BFS-SQL-sql-or-1-eq-1
  • BFS-LLM-llm-ignore-previous
  • BFS-SHELL-shell-rm-rf

Rule IDs are stable across runs; downstream baselines key on them.

Desktop console

The desktop console hosts the engine in two places:

  1. The Tools menu in the title bar opens a global view.
  2. Each project's Security tab group has a project-scoped Bruteforce tab.

The view does not auto-run on load. The user clicks Discover candidate functions to list the project's critical-surface functions (functions that reach a classified sink), picks a subset, configures sink categories / payload classes / confidence floor, and clicks Run bruteforce on N selected to drive the corpus.

Env knobs

VariableDefaultRange
VULKRO_BRUTEFORCE_SINKSunset1 to enable from env
VULKRO_BRUTEFORCE_DEPTH62..=12
VULKRO_BRUTEFORCE_PATHS_PER_FN648..=512
VULKRO_BRUTEFORCE_CATEGORIESunsetcomma-separated tags
VULKRO_BRUTEFORCE_CONFIDENCEhighhigh / medium / low
VULKRO_BRUTEFORCE_PAYLOAD_CLASSESunsetcomma-separated tags

Pairs with

  • Reverse-reach to trace any Bruteforce finding back to its HTTP route / CLI handler.
  • --ai-code-segregation to surface bruteforce findings on AI-touched files first.
  • --gate-vs <ref> to restrict findings to changed lines.