50 · A report's codes: QG and QC¶
A report from stalwart verify or a scan carries findings under two kinds of code. QG codes are the bug
hunter's: ways through your rules, found in the agents' graph, the rulebook and the router. QC codes are
the code checks': problems with how your own tool code handles the network and credentials. This chapter
says how the two differ, lists every QC rule, and says how each family relates to the standards a security
team files findings under.
Language support
The selector reads turns written in English and in Brazilian Portuguese (pt-BR). The language is chosen, never guessed from the turn: the console's Settings page (Language the selector reads), language on a plan request, or the settings.language of your vocabulary file; with none of them, English. A Portuguese turn may be typed with its accents or without them. European Portuguese is not a grammar the selector has, and a turn in any other language is unread.
Two families¶
QG -- the bug hunter |
QC -- the code checks |
|
|---|---|---|
| Reads | the agents' graph, the rulebook or rule sheet, the catalogue, the router in front of them | the source files of the folder agent.yaml is in: Python, JavaScript, TypeScript and Go |
| Runs | on Stalwart's servers, as a hunt job |
on your machine, in the licensed runtime; your code never leaves it, only the count of findings by rule goes with the registration |
| A finding carries | its rule, severity and type, the location in the file you edit, the flow, a reproduction, a checked fix, and its OWASP entries | its rule, its CWE, the file and line, the path the value took to get there, and one sentence |
| Fails the gate | by the gate's conditions: a new Critical finding, a broken row of your own tables, and the others of chapter 28; a project's quality profile can ask for more | every finding, with no level, no waiver and no suppression comment: only changing the code clears it |
| Kept over time | in a project, against its baseline, with a person's decisions -- false positive, accepted risk with an expiry (chapter 51) | not kept: the next run reads the code as it is then |
The first is a question about routes: whether a turn, a document or another agent can make the agent do what your rules say it must not. The second is a question about code: whether a tool sends data in clear text, trusts a server it should check, or keeps a secret where it can leak. A clean answer to one says nothing about the other.
The QG rules¶
The bug hunter has 49 rules, in four ranges:
QG0xxare found in a policy or an agent graph: a way through a rule.QG1xxare review items, security hotspots: a person decides whether each is a problem.QG2xxsay what a scan could not decide or describe.QG201Undecided is the one a clean report must still be read for.QG3xxare found in a host built from your catalogue and the router in front of it.
The rule pages have one page per rule: when it is raised, why it matters, how to fix it, its severity and its OWASP entries. Chapter 28 reads a finding line by line.
The QC rules¶
Every rule the code checks run, from the rule bundle the runtime reads:
Network security
| Rule | CWE | Raised where your code | What clears it |
|---|---|---|---|
QC001 |
CWE-1327 |
opens a listening socket -- a server, a bound port -- other than an MCP server on loopback over the declared http transport |
don't listen from tool code; an MCP server runs on stdio, or on loopback with --transport http |
QC002 |
CWE-295 |
turns TLS verification off or replaces the trust store -- verify=False, rejectUnauthorized: false, InsecureSkipVerify, ssl.CERT_NONE, its own CA bundle |
remove the setting; the system's trust store is the one the checks accept |
QC003 |
CWE-319 |
sends a request over http:// or ws:// to a host that is not loopback |
use https:// or wss:// |
QC004 |
CWE-918 |
sends a request to an address taken from a tool argument or from a response, not checked against a constant list first | fix the host in the code, or check the value against a constant list (if host in ALLOWED) before the request |
QC005 |
CWE-441 |
sends a request to a constant host outside the egress list declared with the catalogue (--egress) |
add the host to the egress list, or remove the request |
QC006 |
CWE-693 |
goes around the environment's proxy -- trust_env=False, a cleared proxy setting, a raw socket |
leave the proxy settings as the environment gives them, and use an HTTP client |
QC007 |
CWE-494 |
runs code it fetched, imports code from a URL, or installs packages at run time | declare the dependency and install it before the agent runs |
Credentials
| Rule | CWE | Raised where your code | What clears it |
|---|---|---|---|
QC010 |
CWE-798 |
writes a secret into the source -- a token, a password, a key in a known format | read it from an environment variable at run time, and revoke the one that was written down |
QC011 |
CWE-532 |
puts a secret in a log line, an exception's message or a tool's result | log and return what the secret was used for, never the secret |
QC012 |
CWE-598 |
puts a secret in a URL's query string | send it in a header |
QC013 |
CWE-312 |
keeps a secret in a store of its own -- a file, a cache, a database | read it from its environment variable each time it is needed |
QC014 |
CWE-522 |
reads a credential from somewhere other than an environment variable -- a keyring, a .env file, a .netrc, a browser's cookies, a credential file such as ~/.aws/credentials |
read it from an environment variable the process is started with |
QC015 |
CWE-522 |
runs its own sign-in flow -- an OAuth exchange, a token request | let the gateway sign in (chapter 37) and pass the tool the token it gets |
Code the checks cannot read
| Rule | CWE | Raised where your code | What clears it |
|---|---|---|---|
QC020 |
-- | cannot be read -- a file that does not parse, minified or generated code, a native extension, a language the checks have no rules for, code run from a string | keep the readable source in the folder, or move what cannot be read out of it |
A secret is a value read from an environment variable whose name holds a word such as token, secret,
password or api_key -- but not one that only says where a secret is, such as TOKEN_FILE -- or a literal
in a known key format. A tool argument is a value the
model or the person gives a tool function. A request is a call of an HTTP client, a socket or a fetch.
QC005 needs an egress list to compare against: stalwart catalogue import takes one with --egress.
stalwart verify has none, so it raises no QC005, and any listening socket is QC001.
Reading a QC finding¶
QC002 CWE-295 shop/tracking.py:8 This code turns TLS verification off (`verify=False`), at shop/tracking.py:8.
shop/tracking.py:8 turns TLS verification off (`verify=False`)
The first line is the rule, its CWE, the place and the sentence. The lines under it are the path: where the value came from, each function it went through, and where it ended. A finding about a secret in a log starts where the secret was read; one about a request's destination starts at the tool argument it came from. Chapter 46 shows this finding in a full report.
The checks follow a value across your functions, so a finding can be in a file no tool imports directly. They read every source file of the folder, tools or not, since a tool can import any of them.
The standards¶
A QG finding carries the OWASP Top 10 for LLM Applications and the OWASP Top 10 for Agentic
Applications entries it is an instance of, with their edition (LLM01:2026, ASI04:2026), in the report and
in the SARIF's taxonomies (chapter 28). Chapter 39 has the
reverse: which rules cover each entry.
A QC finding carries the CWE of the weakness in your code -- CWE-295 is improper certificate
validation -- and no OWASP entry: the OWASP lists are about what an agent can be made to do, and a QC
rule is about how a piece of code is written. QC020 has no CWE: it says the checks could not read the code,
not that the code is weak.
The limits¶
- The code checks read network security and credential handling, in Python, JavaScript, TypeScript and Go, and nothing else about what your code does: a tool that computes the wrong refund passes them.
- Code the checks cannot read is a finding,
QC020, never a pass: a file that does not parse, minified code, a native extension or code run from a string fails the gate until it is readable or gone. - A clean
QCreport is not a cleanQGreport, nor the other way: each answers its own question.