Guide · DerScanner

We Planted SQL Injection in a Delphi App — Cursor Ran the Scan, DerTriage Confirmed It

A step-by-step walkthrough of selective AI verification in DerScanner — issuing an API token, starting a scan with scan-wide AI triage switched off, and running DerTriage on one finding at a time. Executed on a self-hosted installation against a synthetic Delphi application written for this guide.

  • Steps8
  • Time30 minutes
  • AudienceApplication security engineers and developers who triage SAST findings

What this guide covers

How do I run DerTriage on individual SAST findings instead of the whole scan, and drive DerScanner over the API with a token?

Selective verification runs DerTriage against findings you choose, one at a time, from the actions menu of an individual finding. It is a different workload from scan-wide triage, which verifies every finding of the selected severities as part of the scan.

Scan-wide triage is the workload to plan GPU capacity for. Selective verification of a handful of findings is practical on a CPU-only host, which makes it the sensible mode for local installations and for spot-checking a small number of results.

Everything in this guide is done over the DerScanner REST API with a named token and in the web interface. DerScanner also publishes an IDE extension, but no extension is required to drive a scan or to verify a finding.

Before you start

  • A DerScanner installation you can reach over HTTP, and an administrator account that can create users.
  • A user account for this work that is not the administrator account. The screenshots in this guide were taken under a dedicated non-administrator account.
  • A source archive to scan. This guide uses a synthetic application described in step 3.
  • curl, or any HTTP client that can send multipart form data.

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 Delphi application, about 8,000 lines across 23 source files, written specifically for this guide.
Account used for screenshots
Dedicated non-administrator account with scan quotas raised for the evaluation period.

The walkthrough

Step 1

Create a dedicated account for the work

Keep evaluation work, API tokens, and screenshots out of the administrator account.

An administrator account sees every project on the installation and carries permissions that automation does not need. Create a separate user for this work, give it a non-administrative role, and use it for the scan, the token, and the interface.

The account used throughout this guide is a regular user with full access to product functions and no administrative panel. Scan quotas were raised for the evaluation period so that repeated scans would not exhaust a limit mid-guide.

Sign in as that user before continuing. The first sign-in asks the account to accept the end user licence agreement.

Signing in as the dedicated non-administrator account rather than as an administrator.

Step 2

Issue an API token

Drive DerScanner from a script without installing anything into the IDE.

DerScanner publishes an IDE extension for SAST and SCA, and it is a reasonable way to work if you live in the editor. It is not a prerequisite for anything below. A named API token gives the same control from a shell, a pipeline, or any HTTP client.

Request a token with the account credentials and a name you will recognise later. The name matters because tokens are listed and revoked by name.

The response contains an access token. Send it as a bearer token on every subsequent request. Treat it as a credential — it carries the permissions of the account that issued it, so keep it in a secret manager rather than in a shell history or a repository.

Request a named API tokenbash
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=guide-api"

# {"accessToken":"eyJhbGciOiJIUzUxMiJ9..."}

Step 3

Prepare a target whose ground truth you know

Make it possible to judge the verification result rather than trust it.

A verification feature can only be assessed against code whose defects you already know. This guide therefore uses a synthetic application, written for this purpose and for no other. It is not a real product, it was never deployed, and it is not an open-source project belonging to anyone else.

The application is a Delphi warehouse operations tool of about 8,000 lines across 23 units — a login form, an inventory module, orders, suppliers, reporting, backup, and a data access layer. It contains deliberate defects, including SQL statements assembled by string concatenation from values the operator types into the interface.

Package the source as a zip archive. The scan in the next step uploads that archive directly, so nothing needs to be checked into a repository the scanner can reach.

Step 4

Start a scan with scan-wide AI triage switched off

Keep the scan fast and leave verification as a deliberate, separate decision.

Scan settings include AI triage per severity, plus an option to apply the triage result to the finding status automatically. When those are on, every finding of the selected severities is verified as part of the scan. That is the workload to size GPU capacity for, and it is the workload to avoid on a CPU-only host.

Start the scan with those flags explicitly off. The scan then does what a scan does — analyse the code — and leaves every finding unverified for you to choose from.

The response returns the scan identifier you will poll in the next step.

Upload the archive and start a SAST scanbash
TOKEN="$DERSCANNER_TOKEN"
PROJECT="4272a7c0-febb-4ee3-a249-2b72bfe8022c"

curl -s -X POST "https://derscanner.example.com/app/api/v1/scan/start" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@warehouse-ops-delphi.zip" \
  -F "uuid=$PROJECT" \
  -F "languages=CS,CCPP,JAVA,JAVASCRIPT,PASCAL,PHP,PYTHON,REGEX,TSX,TYPESCRIPT" \
  -F "aiTriageCritical=false" \
  -F "aiTriageMedium=false" \
  -F "aiTriageLow=false" \
  -F "aiTriageInfo=false" \
  -F "applyTriage=false"

# {"projUuid":"4272a7c0-...","scanUuid":"4331e444-..."}
The scan list after the API call. No triage step runs, because scan-wide AI triage was disabled in the request.

Step 5

Poll the scan and list what it found

Get to a specific finding identifier you can act on.

Poll the project's scan list until the scan reports COMPLETE. The same record carries the severity counts, so a single request tells you both that the scan finished and how much it found.

This scan reported 65,272 lines of code and two critical findings, both SQL injection. Findings are grouped by the rule that produced them, so a second request returns the rule names with the number of findings under each.

Resolve an individual finding identifier to its position in the source. The response gives the file and line along with the current verification status, which is where the DerTriage verdict will appear later.

Poll the scan, then resolve findings to file positionsbash
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":2}

curl -s -X POST "https://derscanner.example.com/app/api/v1/issues/sources" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "[\"$ISSUE_ID\"]"
# [{"name":"warehouse-ops-delphi/src/uOrders.pas:558","verification":"NOT_PROCESSED"}]
Detailed results for the scan. Both findings are unverified, because no triage ran during the scan.

Step 6

Read the code before asking anything to verify it

Form your own judgement first, so the verdict has something to be checked against.

Open the finding in the interface. The code panel highlights the flagged line together with the surrounding function, which is usually enough to decide whether the data reaching the sink is attacker-controlled.

In this finding the order reference is a string parameter that is concatenated into a SQL statement inside single quotes, with no parameterisation and no escaping. The caller passes the order number the operator selected in the grid or typed into the quick filter, so the value is operator-controlled by design.

That is a genuine injection, and knowing it independently is what makes the next step meaningful.

The flagged sink, src/uOrders.paspascal
function TOrderService.LoadOrder(const AOrderRef: string;
  var AOrder: TOrderRecord): Boolean;
var
  Query: TADOQuery;
begin
  Result := False;
  Query := FDatabase.OpenDataSet('SELECT * FROM orders WHERE number = ''' + AOrderRef + '''');
The flagged line in context. The order reference arrives as a string and is concatenated straight into the statement.

Step 7

Run DerTriage on that one finding

Verify a specific result without starting a scan-wide verification job.

Selective verification is started from the actions menu of an individual finding — the three dots at the right-hand end of the finding row. This is the row that names the file and line, not the row that names the rule and carries the count. Opening the menu on the group row gives you bulk status editing instead.

The menu contains a DerTriage button below the comment field. Pressing it queues verification for that finding alone. The button is replaced by a progress indicator while the job runs, and the verdict appears in the same menu when it completes.

This is the mode that makes local, CPU-only installations useful. One finding is a small job, so a CPU host can return a verdict in a reasonable time, while verifying an entire scan the same way is not a workload to plan without a GPU.

The expanded group. Selective verification is started from the three-dot menu on the individual finding row.

Step 8

Read the verdict and decide

Treat the output as a recommendation that you accept or reject.

The verdict is written into the finding's menu as plain text. For this finding DerTriage reported that the SQL query is constructed by directly concatenating user input, named the parameter it followed, and recommended a status.

The recommendation matches the reading of the code from the previous step — the same parameter, the same mechanism. That agreement is the point of the exercise. Press Apply to record the status on the finding, or set a different status if you disagree.

The verdict names `AOrderRef`, which is the parameter on the flagged line. A verdict that discusses a parameter you cannot find in the code is a signal to look at what context the analyzer actually supplied, not a reason to accept the status.

The verdict as displayed in the interfacetext
DerTriage: The SQL query is constructed by directly concatenating user
input (`AOrderRef`) into a string variable, which can be exploited to
execute malicious SQL commands. DerTriage recommends changing the
vulnerability status to Confirmed.
The verdict for the single verified finding, shown in the same menu the verification was started from.

Checking your result

How do I know the scan really ran without scan-wide triage?

The scan never enters a triage step. A scan with AI triage enabled reports a second step and a triage progress percentage in the scan list; a scan started with the flags off goes straight to COMPLETE, and every finding stays at NOT_PROCESSED until you verify it yourself.

How do I know the verdict belongs to the finding I selected?

The verdict names the identifier from the flagged line — in this walkthrough, the parameter concatenated into the statement. Verification was queued for one finding, so the other finding in the same group remains unverified, which is visible in its status.

How do I confirm the token works before starting a scan?

Request the project's scan list with the bearer token. A 200 response with the scan array confirms both the token and the account's access to that project. A 401 with "No token" means the header never reached the API.

Limits of this procedure

  • DerTriage returns a recommendation, not a proof. It recommends a status; the engineer decides. Verdicts on the same scan can disagree with the code, including recommending rejection for a finding that is genuinely exploitable, so a verdict is a second opinion to weigh against your own reading rather than a substitute for it.
  • The verdict is formed from the code supplied as context for the finding. Where the untrusted value enters in a different unit from the sink, the reasoning is based on the local snippet and may not describe the full data path.
  • Verification time is a property of the host, not of the feature. A memory-constrained DerAI service slows verdicts down by orders of magnitude, and a queued job blocks the ones behind it.
  • Selective verification and scan-wide verification are different workloads. Conclusions about the practicality of one say nothing about the other.
  • The target here is synthetic. It demonstrates the workflow and the shape of the output; it does not indicate detection or verification quality on production code.

Canonical terms used: DerTriage; selective verification; SAST finding; true positive; vulnerability status.

DerScanner

Run this workflow on your own code

Selective verification is available on local installations, including hosts without a GPU. Talk to us about a trial on your codebase.

DerScanner

Request a DerScanner trial

Tell us about the codebase you want to scan and the environment you want to run it in.

DerSecur Recognition · build c8f63ce · 2026-08-24 12:40:25Z · system