What this guide covers
How do I scan AI-generated FastAPI code, keep scan-wide triage off, and verify three individual findings with DerTriage on a CPU-only host?
AI-assisted code generation can produce a working service and a large critical finding count in one pass. Selective verification lets you confirm individual results without running triage across the entire scan.
Scan-wide AI triage is the workload to size for GPU capacity. Verifying a handful of findings one at a time is practical on a CPU-only host running local DerAI, which makes selective mode the sensible choice for local installations and spot checks.
Everything below is driven from the DerScanner REST API with a named token and completed in the web interface under a non-administrator account. No IDE extension is required.
Before you start
- A DerScanner installation you can reach over HTTP, and an administrator account that can create users. The first guide in this series covers account setup and token issuance in more detail.
- A user account for this work that is not the administrator account. The screenshots were taken under a dedicated non-administrator account named Cursor.
- curl, or any HTTP client that can send multipart form data.
- Local DerAI available to the installation. This walkthrough assumes a CPU-only host with no GPU present.
Environment
- DerScanner deployment
- Self-hosted installation, single host, reached at an internal URL. Replace the host in every command with your own.
- DerTriage runtime
- Local DerAI service on the same host, CPU only, no GPU present.
- Scan target
- Synthetic FastAPI supply-desk service, about 1,150 lines across a handful of Python modules, generated from a single Cursor prompt with no hand-edits afterwards.
- Account used for screenshots
- Dedicated non-administrator account with scan and triage quotas raised for the evaluation period.
The walkthrough
Step 1
Generate the application from one prompt
Start from code that was not reviewed line by line before the scan, the way AI-assisted delivery often works.
This guide uses a synthetic FastAPI service written for the walkthrough only. It is not a real product and was never deployed. The entire application was produced from one prompt in Cursor; nothing under app/ was edited by hand afterwards.
The prompt asked for a small internal supply desk — staff login, searchable request lists, CSV reports that can be zipped for email, document upload, saved filter presets from a desktop tool, and a couple of admin endpoints. It deliberately did not ask for vulnerabilities, and it did not ask for tests or Docker.
Keeping the prompt intact matters for provenance. If you reproduce the workflow, save the prompt text alongside the archive you upload, so later readers can see what the model was asked to produce rather than what an engineer added afterwards.
We need a small internal service for the supply desk so the team can stop
tracking requests in spreadsheets. Build it with FastAPI and SQLite.
What it has to do:
- staff log in with a username and password and stay logged in
- list supply requests, with a search box that filters by whichever column the
user picks, and sorting on any column
- a report endpoint that takes a date range and returns a CSV, and can also
package the result as a zip for mailing
- upload and download supplier documents (invoices, delivery notes)
- import a saved filter preset - our desktop tool exports these, so just load
the file it produces
- a couple of admin-only endpoints: re-run the stock sync script and show basic
server stats
Keep it in a handful of files, no ORM, plain SQL is fine - I want to be able to
read the whole thing in one sitting. Skip tests and skip Docker, we'll deal with
that later. We're behind schedule, so just make it work.Step 2
Issue an API token
Drive the scan from a shell without installing anything into the IDE.
DerScanner publishes an IDE extension, but it is not a prerequisite here. A named API token gives the same control from curl, a pipeline, or any HTTP client.
Request a token with the account credentials and a name you will recognise later. Send the access token as a bearer credential on every subsequent request.
The companion guide on selective verification with DerTriage walks through token issuance, session limits, and the first scan in more detail if you need a fuller treatment of those steps.
curl -s -X POST "https://derscanner.example.com/app/api/v1/auth/jwt" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "username=$DERSCANNER_USER" \
--data-urlencode "password=$DERSCANNER_PASSWORD" \
--data-urlencode "token_name=supplydesk-api"
# {"accessToken":"eyJhbGciOiJIUzUxMiJ9..."}Step 3
Start a Python-only scan with scan-wide AI triage switched off
Analyse the code first and leave verification as a separate, deliberate step.
Create a SAST project for the archive, then start the scan with AI triage flags explicitly off for every severity. The scan analyses the code and leaves every finding unverified until you choose otherwise.
Restrict languages to PYTHON so the result set reflects the generated service only. Sending one comma-separated languages field is required on some installations; one form field per language is rejected even when every language is licensed.
The response returns the scan identifier to poll in the next step. The project identifier in the commands below belongs to the synthetic SupplyDesk sample created for this guide.
TOKEN="$DERSCANNER_TOKEN"
PROJECT="8a738067-ba4c-4aee-bd63-faca35cc3280"
curl -s -X POST "https://derscanner.example.com/app/api/v1/scan/start" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@supplydesk-fastapi.zip" \
-F "uuid=$PROJECT" \
-F "languages=PYTHON" \
-F "aiTriageCritical=false" \
-F "aiTriageMedium=false" \
-F "aiTriageLow=false" \
-F "aiTriageInfo=false" \
-F "applyTriage=false"
# {"projUuid":"8a738067-...","scanUuid":"b95a16aa-..."}Step 4
Poll the scan and read the result set
Understand what one prompt produced before verifying anything.
Poll the project scan list until the scan reports COMPLETE. This scan reported 1,167 lines of Python and twenty-seven findings — twenty-two critical and five medium.
Grouped by rule, the scan reported twelve SQL injection findings, three command injection, three deserialization, five path manipulation, two hardcoded passwords, and one each for weak hashing and weak randomness. That volume is the point of selective verification — you choose what to confirm rather than sending the entire set through DerTriage at once.
Resolve individual finding identifiers when you need file and line positions in scripts. The interface groups findings by rule name and expands to show each file and line.
SCAN="b95a16aa-44ff-491c-a3d1-12b2b4d49e13"
curl -s "https://derscanner.example.com/app/api/v1/projects/$PROJECT/scans?offset=0&limit=5" \
-H "Authorization: Bearer $TOKEN"
curl -s "https://derscanner.example.com/app/api/v1/scans/$SCAN/issuesGroupedByRules?offset=0&limit=50" \
-H "Authorization: Bearer $TOKEN"
# {"SQL injection":12,"Command injection":3,"Deserialization of untrusted data":3,...}Step 5
Verify a SQL injection finding
Read the concatenated query first, then ask DerTriage about the sink the scanner traced.
The clearest SQL injection in the generated code is the list endpoint, where search text, column name, status, and sort parameters are concatenated straight into the statement. That is the ground truth to read before trusting any verdict.
The scanner reported the sink at the shared db.query helper rather than at the route handler line. That is normal for inter-procedural analysis — the finding name in the interface points to db.py, while the untrusted values enter in supply.py.
Open the finding in the interface, read the highlighted code, then start selective verification from the three-dot menu on the individual finding row — not the group row that carries the count.
sql = "SELECT * FROM supply_requests WHERE 1=1"
if search:
sql += " AND " + field + " LIKE '%" + search + "%'"
if status:
sql += " AND status = '" + status + "'"
sql += " ORDER BY " + sort + " " + direction
rows = db.query(sql)Step 6
Verify a command injection finding
Confirm a shell invocation built from request parameters.
The report packaging endpoint builds a shell command from the export directory and archive name, then runs it with shell=True. The date range and status values that feed the CSV name also influence the command string.
DerTriage names the mechanism directly. For this finding it points to shell=True and recommends Confirmed, which matches a straight reading of the flagged line.
command = "cd " + config.EXPORT_DIR + " && zip -j -q " + target + " " + csv_name
result = subprocess.run(command, shell=True, capture_output=True, text=True)Step 7
Verify an insecure deserialization finding
Confirm untrusted pickle input on an upload endpoint.
The preset import endpoint accepts a base64-encoded blob from the client and passes it to pickle.loads. That is arbitrary object deserialization on attacker-controlled input — one of the standard reasons pickle is discouraged on untrusted data.
Selective verification is started the same way as for the other findings. The verdict text appears in the finding menu when the job completes.
encoded = payload.get("data") or ""
preset = pickle.loads(base64.b64decode(encoded))Step 8
Where selective verification lives in the interface
Separate the verification action from bulk status editing.
Selective verification is started from the actions menu of an individual finding — the three dots at the right-hand end of the row that names the file and line. Opening the menu on the group row gives bulk status editing instead.
The DerTriage button sits below the comment field in that menu. While the job runs, the button is replaced by a progress indicator. The verdict appears in the same menu when it completes.
You can also queue verification through the REST API with GET /issues/{uuid}/triage?update=true if you prefer to script the step, but the verdict is read the same way — as plain text in the finding menu or in the API response.
Step 9
What three Confirmed verdicts mean here
Separate the recommendation from the decision you record.
All three verifications in this walkthrough ran on the same CPU-only host that serves local DerAI. None of them used scan-wide triage, and none required an IDE extension.
Each verdict is a recommendation, not proof. DerTriage suggested Confirmed for one SQL injection sink, one command injection call, and one pickle.loads — and in each case the recommendation aligned with a direct reading of the flagged code.
Press Apply in the menu to record the status on the finding, or set a different status if you disagree. The value of the exercise is the combination — a fast AI-generated codebase, a selective scan workflow, and a second opinion you can compare against your own reading.
Checking your result
Why restrict the scan to PYTHON only?
The generated service is Python. Restricting languages keeps the result set aligned with the application under review and avoids unrelated findings from other analyzers you did not intend to run on the archive.
How do I know the scan ran without scan-wide triage?
The scan goes straight to COMPLETE without a triage step or triage progress percentage. Every finding stays at Not processed until you verify it yourself or change the status manually.
Can I run selective DerTriage on a CPU-only host?
Yes, for individual findings. This walkthrough verified three findings that way. Scan-wide triage over dozens or hundreds of findings is a different workload and is the one to plan GPU capacity for.
Limits of this procedure
- DerTriage returns a recommendation, not a proof. Verdicts can disagree with the code, including recommending rejection for a finding that is genuinely exploitable, so treat each verdict as a second opinion rather than a substitute for reading the source.
- The verdict is formed from the code supplied as context for the finding. Where the untrusted value enters in a different module from the sink, the reasoning is based on the local snippet and may not describe the full data path in the verdict text.
- Verification time depends on the resources available to DerAI on the host. A memory-constrained service slows individual verdicts down, and queued jobs block the ones behind them.
- The target here is synthetic and generated in one pass. It demonstrates the workflow and the shape of the output; it does not predict detection or verification quality on production codebases.
Related knowledge
Canonical terms used: DerTriage; selective verification; AI-generated code; FastAPI; SQL injection; command injection; insecure deserialization.
DerScanner