Skip to main content
DoneThat

AI Adoption GuideSoftwareTest

Adversarial Input Generator

LLM generates boundary, fuzz, and adversarial inputs targeting known failure modes in the codebase.

Software processDiscoverDesignBuildTestReleaseAdoptSupportRetire

By Don, DoneThat’s AI coach · updated

What this use case covers

An adversarial input generator helps a QA engineer turn known weak spots in the product into concrete test inputs. You feed the model failure modes, relevant code or API contracts, and recent defect notes. It proposes boundary cases, fuzz-style payloads, and adversarial examples aimed at those modes. You decide what enters the suite.

This is not autonomous test authorship. The model drafts candidates. A human still judges relevance, safety, and coverage before anything is committed. Related work often sits beside an Autonomous E2E Test Agent for path coverage, Flaky Test Root Cause Analysis when instability points to race or timing edges, and Risk-Based Test Prioritization when you need to spend review time on the highest-impact areas first.

The value is speed and breadth of idea generation, not replacement of judgment. Good candidates look like something a careful tester would invent after reading the bug history and the code path. Weak candidates are generic payloads with no link to a stated failure mode.

Inputs the model must have

Without a named failure mode and usable code or interface context, the generator should return empty output. Vague prompts such as “find bugs in checkout” are not enough. The model needs enough structure to target something real.

Minimum inputs:

  • Failure modes: Specific statements such as “null optional fields accepted,” “Unicode normalization differs between services,” “pagination off-by-one on last page,” or “rate limiter resets on IP rotation.” Prefer modes drawn from production incidents, bug tickets, threat models, or prior test escapes.
  • Code or contract context: The handler, schema, parser, validator, or API surface under test. Snippets, OpenAPI fragments, protobuf definitions, or validation rules all work. Diffs from a recent change help when the risk is regression-shaped.
  • Constraints: Auth requirements, environment limits, payload size caps, and which channels are in scope (HTTP body, query string, headers, file upload, CLI args, message bus).
  • Existing coverage notes (optional but useful): Cases already in the suite so the model avoids duplicates and focuses on gaps.

If failure-mode text is missing, or the code context is empty or clearly unrelated to the mode, emit no candidates. An empty result is correct behavior. Inventing generic XSS strings or classic fuzz dictionaries without a target mode wastes review time and creates false confidence.

How the generation workflow runs

A practical loop looks like this:

  1. Select a failure mode from an incident, escape, or risk register entry.
  2. Attach the relevant surface: the function, route, schema, or module that should reject or handle the bad input.
  3. Ask for candidates in a fixed shape: input value or payload, channel (body, header, file, and so on), expected observable failure or incorrect acceptance, and a one-line rationale tied to the stated mode.
  4. Filter for emptiness rules: if the model cannot ground a candidate in the provided mode and context, it should skip that candidate rather than pad the list.
  5. Human review: you accept, edit, or discard. Accepted items are written into fixtures, property tests, or parameterized cases by QA or with your normal authoring tools.
  6. Retest and triage: run the new cases. Failures become defects or specification clarifications. Passes still earn their place when they lock a previously escaped path.

Keep batches small. Ten to twenty well-grounded candidates per mode beat a hundred scattershot payloads. Group by mode so reviewers can check whether the set actually stresses that mode from different angles: empty and whitespace, type confusion, length and encoding edges, concurrency or ordering tricks, and privilege or tenancy boundaries when those apply.

What strong candidates look like

Strong adversarial inputs are specific, falsifiable, and mode-linked.

Examples of useful shape (illustrative, not a canned library):

  • For a “trailing slash treated as different resource” mode: paths that differ only by slash, case, or percent-encoding of the same logical ID.
  • For a “decimal money rounding” mode: amounts at banker’s-rounding boundaries, multi-currency pairs, and values that round differently under float vs decimal parsers.
  • For a “multipart upload size check after decode” mode: compressed or encoded bodies whose decoded size exceeds limits while the wire size does not.
  • For a “soft-delete still resolvable by secondary key” mode: IDs and alternate keys that should 404 after delete but historically leaked.

Each candidate should state what “bad success” looks like: silent acceptance, wrong status code, data leak, inconsistent state, or crash. Without an expected wrong outcome, you cannot tell a useful adversarial case from noise.

Avoid dumping classic wordlists unless the failure mode explicitly involves injection or parser confusion on that channel. Classic lists are easy to generate and hard to review. Prefer fewer cases that map cleanly to assertions you can write.

Human review and suite ownership

QA owns the suite. The model proposes; you commit.

Review checklist before adding a candidate:

  • Does it target the named failure mode, or is it a generic payload?
  • Is the input legal to send in the target environment (no destructive production actions, no live-customer data)?
  • Can you express an assertion or oracle (reject, sanitize, isolate, or degrade safely)?
  • Does a similar case already exist under another name?
  • Would a flake-prone timing case belong instead under flaky-test analysis rather than as a static adversarial fixture?

After acceptance, store inputs as fixtures or generators with clear labels: failure mode ID, source ticket, and date added. That metadata makes later pruning and risk-based prioritization possible. When an E2E agent explores flows, adversarial fixtures remain the place for deliberate malice and boundary stress that exploration alone may not invent.

Escalate to a defect when a candidate exposes a real gap. Escalate to a spec question when product and security disagree on expected behavior. Do not silently soften the oracle to make the suite green.

Limits and empty-output rules

This use case fails closed. Empty output is required when:

  • No failure mode is provided, or the mode is only a product area with no concrete wrong behavior.
  • Code, schema, or contract context is missing, truncated beyond use, or clearly mismatched to the mode.
  • Constraints forbid safe generation (for example, only production credentials available and no staging surface).

The generator also should not invent severity scores, pass rates, or industry percentages. It should not claim that a suite is “complete” after one pass. Adversarial coverage grows as failure modes are discovered and as code changes; each run is a batch of proposals for human triage.

Treat the model as a fast junior tester with perfect recall of the snippets you pasted and no institutional memory beyond that. Your job is to supply the modes and context, reject ungrounded ideas, and land the keepers in the suite with assertions that will still make sense six months later.

Is this worth automating for you?

Whether this pays back depends on how much time it takes your team today. Most teams estimate that from memory, and the estimate is usually wrong in one direction or the other.

DoneThat reconstructs where the time actually went, with no timers to forget, so you can measure the baseline before committing to a project and check the gain afterward.

Measure the baseline first