Skip to content

AsyncAPI 3.x: taint the payloads of AsyncAPI messages - #1870

Open
LautaroPetaccio wants to merge 1 commit into
feature/asyncapi-black-box-coveragefrom
feature/asyncapi-taint
Open

LautaroPetaccio wants to merge 1 commit into
feature/asyncapi-black-box-coveragefrom
feature/asyncapi-taint

Conversation

@LautaroPetaccio

@LautaroPetaccio LautaroPetaccio commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Third in the AsyncAPI white-box stack, on top of #1869. White-box AsyncAPI runs use taint analysis, on by default as it is for REST, GraphQL, RPC and Web runs.

Why

Taint analysis is how the search learns the constants a service compares its inputs with: a string gene carries a recognisable value, the instrumented SUT reports what it compared that value with, and the gene keeps the constant for mutation to use. AsyncApiFitness never ran it, so a service that branches on a payload field equal to some constant left the search guessing.

What it adds

One call in AsyncApiFitness: TaintAnalysis.doTaintAnalysis after the coverage is read, under the same conditions as REST and GraphQL: white-box (after #1869, the only mode that reads coverage), during the search, and with taint analysis enabled.

Nothing else was missing:

  • payload and header strings are StringGenes, which already take tainted values at sampling and mutation time;
  • they travel to the service inside the message, as the payload JSON and the header map;
  • the instrumentation checks for tainted values the same way whatever the problem type;
  • the controller records one AdditionalInfo entry per action it is told about, since EMController.newAction sets the action before branching on the call type.

Two things that are less obvious than they look

  • The size check is against the call results, not the equality GraphQL asserts. additionalInfoList may have no more entries than there are AsyncApiCallResults. A test stops at the first message that could not be published, so there can be fewer results than actions, and a driver that could not be reached records no entry for its message, so there can be fewer entries than results. TaintAnalysis itself only refuses more.
  • The action index in the instrumentation is global, not per thread. A comparison made on a consumer thread is recorded against whichever action is current when it happens. This is harmless, because the analysis matches each reported value against the genes of every action, not only those of the action the entry belongs to.

Not here

An E2E that shows taint reaching a branch only it can reach needs a SUT that compares a payload field with a constant. It comes with the fire-and-forget Spring Kafka E2E later in the stack.

Testing

  • AsyncApiFitnessTest, 4 new tests:
    • a payload string carrying a tainted value gains the constant from the AdditionalInfoDto a FakeAsyncApiDriver reports, and the tainted value is in the payload the driver was asked to publish;
    • a test of three messages stopped by a failed publish on the second, with two entries for three actions, is still analysed (this one fails with an equality check);
    • a black-box run under --algorithm MIO, where taint analysis is enabled, never asks for coverage and so never analyses;
    • an evaluation outside the search does not analyse.
  • NcsKafkaEMTest (Docker): 3 tests, 0 failures.
  • All AsyncAPI suites plus the config ones and StatisticsTest: 291 tests, 0 failures.

REST, GraphQL, RPC and Web all run taint analysis in white-box mode:
a string gene carries a recognisable tainted value, the instrumented
SUT reports what it compared that value with, and the gene learns the
constant for mutation to use. AsyncAPI never did, so a service that
branches on a payload field equal to some constant left the search
guessing.

Nothing else was missing. Payload and header strings are StringGenes
that already take tainted values at sampling and mutation time, they
travel to the service inside the message, the instrumentation reports
what it saw regardless of the problem type, and the controller records
one AdditionalInfo entry per action it is told about. So
AsyncApiFitness calls TaintAnalysis.doTaintAnalysis after reading the
coverage, under the same conditions as REST and GraphQL: white-box,
during the search, with taint analysis enabled, which it is by
default.

Worth flagging for review:

 - The size check is against the call results, not the equality
   GraphQL asserts: no more entries than AsyncApiCallResults. A test
   stops at the first message that could not be published, so there
   can be fewer results than actions, and a driver that could not be
   reached records no entry for its message, so there can be fewer
   entries than results. TaintAnalysis itself only refuses more.
 - The action index in the instrumentation is global, so a comparison
   made on a consumer thread is recorded against whichever action is
   current. This is harmless: the analysis matches each reported value
   against the genes of every action.

AsyncApiFitnessTest covers a payload string gaining the constant from
the AdditionalInfoDto a fake driver reports, a test stopped by a
failed publish with fewer entries than actions, a black-box run under
MIO (where taint analysis is enabled) that never asks for coverage and
so never analyses, and an evaluation outside the search that does not
analyse either.

Black-box runs are unchanged, as are all other problem types.
@jgaleotti
jgaleotti requested a review from arcuri82 October 13, 2026 19:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants