One credential, found once, tried everywhere.
Here's a representative run. Nothing about this is hypothetical; it's the same static-to-live handoff that runs every time an agentic pen test starts.
Trojan's secrets engine flags a hardcoded password in a config file, a plain SAST/secrets finding, exactly like any scanner would report on its own.
Instead of closing the finding, the pen-test agent calls its remember_fact tool: kind “credential,” value the password, from the file it came from. This is the one thing a static scanner never does: carry a finding forward.
While crawling the live target, the agent finds a login form (maybe an admin panel, maybe a second service entirely) and checks its memory of facts against what it's looking at.
Inside the engagement's safe-mode bounds, the agent sends the login attempt itself, the same move a real attacker makes after finding a leaked password.
A successful login isn't just “confirmed.” It's remembered as a new Fact (an authenticated session), which the agent can chain again: what does this account expose access to?
The report doesn't say “hardcoded secret, medium severity” five times over. It shows the path: this file leaked a password, which opened this login, which reached this data, ranked by what it actually proved, not just what it noticed.
Everything the agent learns is a Fact.
Not every finding is a credential. A Fact is anything worth carrying forward, and it's what separates a pen test from a checklist.
A Fact records where it came from and what it might unlock: from and enables. Those links are what render as the kill-chain edges on the live attack graph while a run is in progress.
A checklist stops at the first line. An attacker doesn't.
| A scanner (and most pen-test tools) | Trojan's agent | |
|---|---|---|
| Finds a hardcoded secret | Reports it. Done. | Reports it, then tries it |
| Finds an exposed admin route | Flags the route exists | Checks what that credential (or a leaked token) gets you there |
| Two findings, related | Two line items, same severity math applied to each | One chain, ranked by what it actually proved is reachable |
| What you get | A list to triage | A path an attacker would take, with each step's evidence attached |
The agent has an explicit tool call for this; it isn't inferred after the fact:
remember_fact({
kind: "credential",
summary: "hardcoded password found in config.py:14",
value: "••••••••••", // retained for reuse, never shown in the UI
from: "config.py:14",
})Every probe the agent sends afterward runs inside the engagement's safe-mode bounds: rate-limited, scoped to what you allow-listed, never a destructive request. Chaining finds the path further; it doesn't take the engagement further than you consented to.