Haijun Platform Docs
ID

Security teams want to find memory-safety bugs before attackers do, but the existing tooling makes it hard: static analyzers produce so many false positives that reviewers stop reading, and fuzzers need a hand-written harness per entry point before they find anything. This cookbook shows how to use the

Haijun Agent SDK

to build a vulnerability-discovery agent that reads source code with Haijun Code's built-in

Read

,

Grep

, and

Glob

tools, reasons about which inputs could corrupt memory, and writes findings a reviewer can act on.

By the end of this cookbook, you'll be able to:

ad of hand-rolled file access

Chain find, triage, and report as separate

query()

calls that emit schema-conformant JSON

Prerequisites

h2>

We define an ENGAGEMENT_CONTEXT block that we pass as system_prompt on every agent. It records the scope of this assessment (authorized by the code owner, isolated read-only sandbox, findings headed for responsible disclosure) so every step in the pipeline operates against the same documented ground rules. Keep those three claims true for any real target.

hat terminates after a `ResultMessage`.

This is the same `async for msg in ...` loop the other notebooks in this

series write inline; it is factored out here because this notebook runs

the loop four times (TM bootstrap, TM interview, find, triage) and the

`isinstance` ladder would otherwise repeat verbatim.

"""

final = ""

async for msg in stream:

if isinstance(msg, AssistantMessage):

for block in msg.content:

if isinstance(block, ToolUseBlock):

args = str(block.input)

args = args if len(args) <= 120 else args[:120] + "...}"

print(f" [tool] {block.name} {args}")

elif isinstance(block, TextBlock) and block.text.strip():

final += block.text

elif isinstance(msg, ResultMessage) and msg.is_error:

raise RuntimeError(msg.result)

return final

print(f"Model: {MODEL_NAME}")

Model: haijun-opus-4-7 Step 2: Load the canary target vulnerability_detection_agent/canary/canary.c is a ~45-line C program with three deliberately planted memory-safety bugs (a heap buffer overflow, a stack buffer overflow, and a use-after-free), each reachable through a different "magic byte" at the start of the input. The bugs aren't labeled; the find agent in Step 4 has to locate them by reading the logic the same way it would in real code. When you're ready to try your own code, point TARGET_DIR at your checkout.

file → heap | freed chunk metadata, tcache/fastbin state | ## 4. Threats | id | threat | surface | asset | impact | likelihood | | --- | --- | --- | --- | --- | --- | | T1 | Heap buffer overflow: parse_alpha copies up to n-1 (≤4095) bytes into a 32-byte malloc without bounds check (canary.c:8-9) | A-prefixed input | process memory | heap corruption → potential RCE | high given a malicious file | | T2 | Stack buffer overflow: parse_bravo copies up to n-1 bytes into a 16-byte stack array (canary.c:15-16); the trailing name[15]=0 does not prevent the overflow, only truncates the printed string | B-prefixed input | saved return address / stack | classic stack smash → RCE if no/bypassed stack protector | high | | T3 | Use-after-free / double-free vector: parse_charlie frees p when data[0]==0xff then immediately memcpys into the freed chunk (canary.c:23-26); the subsequent printf also leaks the freed pointer | C-prefixed input with first payload byte 0xff | heap allocator metadata | UAF write → heap grooming, info leak of freed address | medium–high | | T4 | Unchecked malloc return in parse_alpha and parse_charlie (canary.c:8, 22) | any A or C input under memory pressure | process stability | NULL-deref DoS | low in normal ops | | T5 | Pointer disclosure via printf("%p", p) in parse_charlie (canary.c:27) | any C input | ASLR secret | leaks heap address, aids exploitation of T1/T3 | high when combined with T1/T3 | | T6 | Path handling of argv[1]: no canonicalization or allow-list; process will open any readable path including symlinks and device nodes | caller-controlled argv | host files | info disclosure or hang (e.g., /dev/zero) depending on who runs it | unknown — depends on invoker privilege | | T7 | Silent truncation: only the first 4096 bytes are read; parsers operate on truncated data, which can mask malformed-file detection upstream | any input | integrity of downstream decisions | logic bug, not memory-safety | low | ## 5. Open questions - Who supplies argv[1]? Local user only, a setuid wrapper, a web upload handler, an MTA, a sandbox runner? This determines whether T1–T3 are local-only footguns or remote code-execution primitives. - What uid / capabilities / container does the binary run as? Blast radius of successful RCE hinges on this (root vs. nobody vs. seccomp-confined). - Compiler and link flags in production builds: is -fstack-protector-strong, -D_FORTIFY_SOURCE=2, PIE, RELRO, ASLR, or CFI enabled? These materially change exploitability of T2 and the usefulness of T5's leak. - Allocator in use (glibc ptmalloc, musl, jemalloc, hardened_malloc). T3's UAF exploit primitives differ by allocator. - Is canary invoked directly or via a wrapper that validates the file first? A front-end parser / size cap / magic-byte allow-list upstream could neutralize T1–T3. - Are crashes monitored? Unchecked malloc (T4) and T6 DoS matter only if uptime is a requirement; source alone does not say. - Intended threat model for the format itself: are A/B/C formats specified anywhere (docs, RFC, sibling files) so we can tell "malformed" from "adversarial"? - Expected lifetime and distribution of the binary: is this a demo/test fixture (the name "canary" suggests so) or shipped to customers? Affects remediation priority and disclosure path. [tool] Write {'file_path': 'vulnerability_detection_agent/canary/THRE...} --- refined THREAT_MODEL.md --- # Threat Model: canary (local file-format parser CLI) ## 1. System context canary is a local command-line utility invoked as ./canary . It opens the supplied path, reads up to 4096 bytes, and dispatches on the first byte (A/B/C) into one of three parsers. It is not network-facing. The input file is fully attacker-controlled in the intended threat model — typical delivery is a file received by email or downloaded from the web and then opened by the local user. The process runs as the invoking user with no sandboxing, so any memory-safety bug that yields control flow yields code execution in that user's security context (home directory, SSH keys, browser profile, cloud credentials, any writable path the user has). ## 2. Assets | asset | description | sensitivity | | --- | --- | --- | | invoking user's account | uid, home directory, shell history, SSH keys, browser profile, cloud tokens, dotfiles, writable mounts | high — compromise equals full user takeover and a foothold for lateral movement | | process memory / control flow | heap and stack of the canary process, return addresses, heap metadata | high — corruption is the exploitation primitive for the user-account asset | | input file contents | bytes read from argv[1]; format selector plus parser payload | low as data; the primary attack vector | | host integrity | persistence locations writable as the user (crontab, ~/.bashrc, ~/.config/systemd/user, login items) | high — trivially reachable post-exploitation; no sandbox to contain it | ## 3. Entry points & trust boundaries | entry_point | description | trust_boundary | reachable_assets | | --- | --- | --- | --- | | argv[1] path | caller-supplied filesystem path opened with fopen(..., "rb") | local user → process (same uid; the boundary is between the untrusted file content and the parser, not between the caller and process) | any file readable by the user | | file contents (first 4096 bytes) | fread into buf; dispatch on buf[0] | untrusted attacker (file author) → parser | process memory via all three parsers | | parse_alpha payload (A prefix) | bytes 1..n copied via memcpy into a 32-byte heap buffer | untrusted file → heap | heap adjacent to 32-byte allocation | | parse_bravo payload (B prefix) | bytes 1..n copied into a 16-byte stack buffer | untrusted file → stack | return address, saved frame pointer, stack canary (if enabled) | | parse_charlie payload (C prefix) | bytes 1..n copied into a 64-byte heap buffer; first byte may trigger early free | untrusted file → heap | freed-chunk metadata, tcache/fastbin state | ## 4. Threats | id | threat | surface | asset | impact | likelihood | | --- | --- | --- | --- | --- | --- | | T1 | Heap buffer overflow: parse_alpha copies up to n-1 (≤4095) bytes into a 32-byte malloc without bounds check (canary.c:8-9) | A-prefixed attacker file | process memory → user account | critical — RCE as the invoking user; full account takeover, no sandbox containment | high — trivially reachable with a single crafted file | | T2 | Stack buffer overflow: parse_bravo copies up to n-1 bytes into a 16-byte stack array (canary.c:15-16); trailing name[15]=0 only truncates the print, does not prevent the overflow | B-prefixed attacker file | saved return address → user account | critical — classic stack smash to RCE as the user; exploitability depends on stack-protector / PIE / ASLR in the shipped build | high | | T3 | Use-after-free / double-free: parse_charlie frees p when data[0]==0xff then immediately memcpys into the freed chunk (canary.c:23-26) | C-prefixed attacker file with first payload byte 0xff | heap allocator metadata → user account | critical — heap-grooming primitive for RCE as the user | medium–high — reliability varies by allocator but the primitive is clean | | T4 | Unchecked malloc return in parse_alpha and parse_charlie (canary.c:8, 22) | any A or C input under memory pressure | process stability | low — NULL-deref crash of a user-invoked CLI; annoyance, not compromise | low | | T5 | Pointer disclosure via printf("%p", p) in parse_charlie (canary.c:27) | any C input | ASLR secret | high — directly hands the attacker a heap address, making T1/T3 reliable even with ASLR | high — unconditional on the C path | | T6 | Path handling of argv[1]: no canonicalization or allow-list; canary opens any path the user can read, including symlinks and device nodes (e.g., /dev/zero hang) | caller-supplied argv | process stability / caller expectations | low — the caller is already the user, so there is no privilege boundary to cross via path tricks | low | | T7 | Silent truncation: only the first 4096 bytes are read; malformed-file detection upstream can be bypassed by padding the exploit into the first 4 KB | any input | integrity of any upstream "scan then open" pipeline | low on its own; relevant if a scanner is put in front | low | | T8 | (new) Social-engineering delivery: the attack surface is "user opens a file." Typical vectors are email attachments, messenger drops, and browser downloads. Any of T1/T2/T3 becomes a one-click RCE given a convincing lure | attacker-authored file delivered to the user | user account | critical — same as T1–T3; this threat names the delivery mechanism | high — the dominant real-world path to triggering T1–T3 | | T9 | (new) File-association / handler registration: if canary is (or becomes) the registered handler for a file extension or MIME type, double-clicking a download auto-invokes it, removing the need to coach the user into a shell command | OS file-association layer | user account | critical — converts T8 from "run this binary on this file" to "open the attachment" | unknown without deployment config; flagged for owner | | T10 | (new) Post-exploitation blast radius: no sandbox, so RCE in canary immediately has the user's full ambient authority — SSH keys, browser cookies/session tokens, cloud CLI credentials (~/.aws, ~/.config/gcloud), persistence via ~/.bashrc, user-level cron, user systemd units, login items | any of T1/T2/T3 succeeding | user account, connected systems | critical — lateral movement into email, cloud, source control is trivial from here | high conditional on T1/T2/T3 | | T11 | (new) Corpus / fuzzing exposure: with three obvious memory bugs and a one-byte dispatch, the first hour of afl-fuzz or libFuzzer against canary will produce crashing inputs. If the binary is distributed, researchers and attackers will find these quickly | any attacker with the binary | disclosure timeline | high — forces a short remediation window | high | ## 5. Open questions All prior open questions are resolved by the owner's answers, except the following residual items, which are narrower and still code- or build-dependent: - Shipped compiler / linker hardening: is the distributed build compiled with -fstack-protector-strong, -D_FORTIFY_SOURCE=2, -fPIE/-pie, full RELRO, and with ASLR enabled on the target OS? Changes exploit reliability of T2 and the value of T5's leak, but not the severity class. - Allocator in the shipped build (glibc ptmalloc vs. musl vs. hardened allocator): determines which T3 exploitation primitives are practical. - File-association registration (T9): does any installer or desktop entry register canary as a handler for an extension or MIME type? If yes, T8 collapses into a pure double-click RCE. - Signed / notarized distribution: is the binary shipped in a way that OS gatekeepers (macOS Gatekeeper, Windows SmartScreen, Linux desktop "executable bit" prompts) would warn the user before first run? Affects the friction on T8 but not the final impact. - Telemetry / crash reporting: are crashes from T1–T4 reported anywhere the defender would see them, or does a failed exploit attempt go unnoticed?

Step 4: Run the agentic find loop

On this page
PrerequisitesStep 4: Run the agentic find loop