Driving Semgrep Desk from your own code
Everything the web page does is one HTTP API. The base URL is https://api.skillsafe.ai/v1/app-api, every call carries Authorization: Bearer <token>, and every response is the same envelope.
The task field comes first
Semgrep Desk is one app with four lanes over the same rule. Every request must carry a task field; it is what routes the run. The four values are:
| task | What you get | Derived from |
|---|---|---|
audit | The rule audit: an operator inventory, findings, an ordered edit plan, and the corrected rule file. | @trailofbits/semgrep-rule-creator |
fp-triage | The false-positive triage: a per-line verdict on the annotated sample, the narrowest exclusions, and the rule with them applied. | @trailofbits/fp-check |
variants | The bypass variants: a vector table with caught-now / caught-after columns, and the extended rule. | @trailofbits/semgrep-rule-variant-creator |
hunt | The variant hunt: search targets with real queries, and a Markdown hunting plan. | @trailofbits/variant-analysis |
If task is missing or unrecognised the model answers the closest lane and names its choice in lane and in the first sentence of summary - it never blends two lanes into one answer. Read lane off the reply rather than assuming it echoes what you sent.
The envelope
Every response is {"ok": true, "data": {...}} or {"ok": false, "error": {"code": "...", "message": "..."}}. Read data; on a non-2xx status read error.code.
| HTTP | error.code | What it means |
|---|---|---|
400 | VALIDATION_ERROR | The body was not valid JSON, or a required field was missing. |
401 | UNAUTHENTICATED | No token, or a token that has expired. Mint a new guest token, or sign in. |
402 | INSUFFICIENT_CREDITS | The balance is below min_credits. Call /estimate first and compare against /me. |
404 | NOT_FOUND | Wrong path. Every path on this page is exactly as written — note that only /guest names the app slug, and it does so in the body, never in the URL. |
409 | IDEMPOTENCY_CONFLICT | The same Idempotency-Key was reused with a different body. |
429 | RATE_LIMITED | Back off and retry. Never tight-loop. |
503 | UPSTREAM_UNAVAILABLE | The model provider is unreachable. Retry with backoff. |
Step 1 - get a token
A guest token is minted per app and needs no account; it is enough for /me and /estimate, and for a run only if the publisher sponsors guests (this app does not). For a real run, sign in on the token page and copy the token from there. This is the only endpoint that names the app slug, and it does so in the body, never in the URL. Every snippet below reuses the small client helper defined here. Keep your token out of source control - read it from a secrets manager or an environment variable rather than pasting it in, and treat every YOUR_TOKEN placeholder on this page as a reminder to do so.
TOKEN=$(curl -sS -X POST https://api.skillsafe.ai/v1/app-api/guest \
-H "Content-Type: application/json" \
-d '{"slug": "semgrep-desk"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["token"])')
echo "$TOKEN"
import json, urllib.request
BASE = "https://api.skillsafe.ai/v1/app-api"
def post(path, payload, token=None, headers=None):
data = json.dumps(payload).encode()
h = {"Content-Type": "application/json"}
if token:
h["Authorization"] = "Bearer " + token
h.update(headers or {})
req = urllib.request.Request(BASE + path, data=data, headers=h)
with urllib.request.urlopen(req) as r:
return json.load(r)["data"]
token = post("/guest", {"slug": "semgrep-desk"})["token"]
print(token)
const BASE = "https://api.skillsafe.ai/v1/app-api";
async function call(path, { method = "POST", body, token, headers = {} } = {}) {
const res = await fetch(BASE + path, {
method,
headers: {
"Content-Type": "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
...headers,
},
body: body === undefined ? undefined : JSON.stringify(body),
});
const json = await res.json();
if (!res.ok) throw new Error(json.error?.code + ": " + json.error?.message);
return json.data;
}
const { token } = await call("/guest", { body: { slug: "semgrep-desk" } });
console.log(token);
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
const base = "https://api.skillsafe.ai/v1/app-api"
func call(path, token string, payload any, extra map[string]string) (map[string]any, error) {
b, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", base+path, bytes.NewReader(b))
req.Header.Set("Content-Type", "application/json")
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
for k, v := range extra {
req.Header.Set(k, v)
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var out struct{ Data map[string]any }
json.NewDecoder(res.Body).Decode(&out)
return out.Data, nil
}
func main() {
d, _ := call("/guest", "", map[string]string{"slug": "semgrep-desk"}, nil)
fmt.Println(d["token"])
}
import java.net.URI;
import java.net.http.*;
public class Desk {
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
static final HttpClient HTTP = HttpClient.newHttpClient();
static String post(String path, String json, String token) throws Exception {
HttpRequest.Builder b = HttpRequest.newBuilder(URI.create(BASE + path))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json));
if (token != null) b.header("Authorization", "Bearer " + token);
return HTTP.send(b.build(), HttpResponse.BodyHandlers.ofString()).body();
}
public static void main(String[] a) throws Exception {
System.out.println(post("/guest", "{\"slug\":\"semgrep-desk\"}", null));
}
}
require 'json'
require 'net/http'
BASE = URI('https://api.skillsafe.ai/v1/app-api')
def post(path, payload, token = nil, extra = {})
uri = URI(BASE.to_s + path)
req = Net::HTTP::Post.new(uri, { 'Content-Type' => 'application/json' }.merge(extra))
req['Authorization'] = "Bearer #{token}" if token
req.body = JSON.generate(payload)
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
JSON.parse(res.body)['data']
end
token = post('/guest', { slug: 'semgrep-desk' })['token']
puts token
<?php
$BASE = "https://api.skillsafe.ai/v1/app-api";
function call_api($path, $payload, $token = null, $extra = []) {
global $BASE;
$headers = array_merge(["Content-Type: application/json"], $extra);
if ($token) { $headers[] = "Authorization: Bearer " . $token; }
$ch = curl_init($BASE . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
return $out["data"];
}
$token = call_api("/guest", ["slug" => "semgrep-desk"])["token"];
echo $token . "\n";
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var http = new HttpClient { BaseAddress = new Uri("https://api.skillsafe.ai/v1/app-api/") };
async Task<JsonElement> Call(string path, object payload, string token = null,
(string, string)? extra = null) {
var req = new HttpRequestMessage(HttpMethod.Post, path) {
Content = new StringContent(JsonSerializer.Serialize(payload),
Encoding.UTF8, "application/json")
};
if (token != null)
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
if (extra is (string k, string v)) req.Headers.Add(k, v);
var res = await http.SendAsync(req);
var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
return doc.RootElement.GetProperty("data");
}
var token = (await Call("guest", new { slug = "semgrep-desk" })).GetProperty("token").GetString();
Console.WriteLine(token);
Step 2 - check the balance
GET /me returns the subject and its credit balance. Compare it against hold_credits from step 3 before you submit a run: a 402 after submitting is a failure of your client, not of the service.
curl -sS https://api.skillsafe.ai/v1/app-api/me -H "Authorization: Bearer $TOKEN"
req = urllib.request.Request(BASE + "/me",
headers={"Authorization": "Bearer " + token})
with urllib.request.urlopen(req) as r:
me = json.load(r)["data"]
print(me)
const me = await call("/me", { method: "GET", token });
console.log(me);
req, _ := http.NewRequest("GET", base+"/me", nil)
req.Header.Set("Authorization", "Bearer "+token)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
var out struct{ Data map[string]any }
json.NewDecoder(res.Body).Decode(&out)
fmt.Println(out.Data)
HttpRequest req = HttpRequest.newBuilder(URI.create(BASE + "/me"))
.header("Authorization", "Bearer " + token).GET().build();
System.out.println(HTTP.send(req, HttpResponse.BodyHandlers.ofString()).body());
uri = URI(BASE.to_s + '/me')
req = Net::HTTP::Get.new(uri)
req['Authorization'] = "Bearer #{token}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)['data']
$ch = curl_init($BASE . "/me");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $token],
]);
$me = json_decode(curl_exec($ch), true)["data"];
curl_close($ch);
print_r($me);
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
var me = JsonDocument.Parse(await http.GetStringAsync("me"))
.RootElement.GetProperty("data");
Console.WriteLine(me);
Step 3 - price the run (free)
POST /estimate costs nothing, creates no job, and returns model, model_alias, markup_bps, hold_credits, min_credits and sponsor_enabled. The hold prices the full output cap; the charge is usually far lower. Estimate the lane you are about to run - the hold differs per lane because the prompts and output caps differ.
curl -sS -X POST https://api.skillsafe.ai/v1/app-api/estimate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"task": "audit", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}'
payload = {
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
estimate = post("/estimate", payload, token)
print(estimate)
const payload = {
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
};
const estimate = await call("/estimate", { body: payload, token });
console.log(estimate);
var payload map[string]any
json.Unmarshal([]byte(`{"task": "audit", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}`), &payload)
estimate, _ := call("/estimate", token, payload, nil)
fmt.Println(estimate)
String payload = """
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""";
System.out.println(post("/estimate", payload, token));
payload = JSON.parse(<<~'JSON')
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON
estimate = post('/estimate', payload, token)
puts estimate
$payload = json_decode(<<<'JSON'
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON, true);
$estimate = call_api("/estimate", $payload, $token);
print_r($estimate);
var payload = JsonSerializer.Deserialize<object>("""
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""");
var estimate = await Call("estimate", payload, token);
Console.WriteLine(estimate);
The input fields
| Field | Type | Meaning |
|---|---|---|
task | string, required | One of audit, fp-triage, variants, hunt. Routes the run. If it is missing or unrecognised the model answers the closest lane and names its choice in lane. |
rule | string, required | The Semgrep rule file. The web page sends a digest: the rule YAML in a fenced block, the sample in a second block, and a short inventory of what its browser-side reader already established. Sending the raw YAML alone works too. |
sample | string | Code the rule is meant to fire on. May carry Semgrep test annotations (ruleid, ok, todoruleid, todook). The web page folds this into rule; either field is read. |
intent | string | One or two sentences on what the rule is supposed to catch. This is what the audit lane measures the rule against. |
target | string | pr-blocking, ci-advisory, local-dev or registry. |
tolerance | string | noise-averse, balanced or miss-averse. Changes the recall/precision advice. |
context | string | Anything else worth knowing: frameworks, existing wrappers, repository size. |
prescan_facts | object | What a reader already established. risk_tier, flags[] (id, severity, rule, detail), rules[], annotations[], stats. Every flag id you send here must come back exactly once in coverage_check. Optional — omit it and the model reads the rule unaided. |
upstream | string | The previous lane's output, when you are chaining lanes. |
retry_note | string | Optional. Sent only when a previous reply failed to parse, to tell the model exactly what to correct. The prompt is instructed to obey it. Reuse the same Idempotency-Key as the attempt it is retrying, or the retry is billed as a second run. |
sample | string | Always sent as the empty string by the web app, which folds the code sample into rule under a ## The code sample heading. You may send it either way; the prompt reads the sample from rule and its parsed annotations from prescan_facts.annotations[]. |
Step 4 - run it, and poll
POST /run returns a job_id immediately. Always send an Idempotency-Key: a retried request with the same key returns the same job instead of billing twice. The web page derives its key from the lane plus a hash of the input plus an attempt counter, which is why two lanes over the same rule are two distinct runs.
curl -sS -X POST https://api.skillsafe.ai/v1/app-api/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: semgrep-desk:audit:f2834bd4:a1" \
-d '{"task": "audit", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}'
payload = {
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
job = post("/run", payload, token, headers={"Idempotency-Key": "semgrep-desk:audit:f2834bd4:a1"})
print(job)
const payload = {
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
};
const job = await call("/run", { body: payload, token, headers: { "Idempotency-Key": "semgrep-desk:audit:f2834bd4:a1" } });
console.log(job);
var payload map[string]any
json.Unmarshal([]byte(`{"task": "audit", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}`), &payload)
job, _ := call("/run", token, payload, map[string]string{"Idempotency-Key": "semgrep-desk:audit:f2834bd4:a1"})
fmt.Println(job)
String payload = """
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""";
System.out.println(post("/run", payload, token));
payload = JSON.parse(<<~'JSON')
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON
job = post('/run', payload, token, { 'Idempotency-Key' => 'semgrep-desk:audit:f2834bd4:a1' })
puts job
$payload = json_decode(<<<'JSON'
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON, true);
$job = call_api("/run", $payload, $token, ["Idempotency-Key: semgrep-desk:audit:f2834bd4:a1"]);
print_r($job);
var payload = JsonSerializer.Deserialize<object>("""
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""");
var job = await Call("run", payload, token, ("Idempotency-Key", "semgrep-desk:audit:f2834bd4:a1"));
Console.WriteLine(job);
Then poll GET /jobs/{job_id} until status is succeeded or failed. The reply text is at output.output, as a JSON string you parse a second time.
curl -sS https://api.skillsafe.ai/v1/app-api/jobs/job_abc123 -H "Authorization: Bearer $TOKEN"
req = urllib.request.Request(BASE + "/jobs/job_abc123",
headers={"Authorization": "Bearer " + token})
with urllib.request.urlopen(req) as r:
job = json.load(r)["data"]
print(job)
const job = await call("/jobs/job_abc123", { method: "GET", token });
console.log(job);
req, _ := http.NewRequest("GET", base+"/jobs/job_abc123", nil)
req.Header.Set("Authorization", "Bearer "+token)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
var out struct{ Data map[string]any }
json.NewDecoder(res.Body).Decode(&out)
fmt.Println(out.Data)
HttpRequest req = HttpRequest.newBuilder(URI.create(BASE + "/jobs/job_abc123"))
.header("Authorization", "Bearer " + token).GET().build();
System.out.println(HTTP.send(req, HttpResponse.BodyHandlers.ofString()).body());
uri = URI(BASE.to_s + '/jobs/job_abc123')
req = Net::HTTP::Get.new(uri)
req['Authorization'] = "Bearer #{token}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)['data']
$ch = curl_init($BASE . "/jobs/job_abc123");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $token],
]);
$job = json_decode(curl_exec($ch), true)["data"];
curl_close($ch);
print_r($job);
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
var job = JsonDocument.Parse(await http.GetStringAsync("jobs/job_abc123"))
.RootElement.GetProperty("data");
Console.WriteLine(job);
Step 5 - or stream it
POST /run-stream is the same call over Server-Sent Events. Frames are event: delta with {"text": "..."}, then one event: done carrying {job_id, status, charged_credits, output}. On an idempotent replay the server answers with plain JSON rather than an event stream, so check the response Content-Type before you start parsing frames. The same Idempotency-Key rule applies.
curl -sS -N -X POST https://api.skillsafe.ai/v1/app-api/run-stream \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: semgrep-desk:audit:f2834bd4:a1" \
-d @input.json
# event: delta
# data: {"text":"{\"lane\":\"audit\""}
# event: done
# data: {"job_id":"job_...","status":"succeeded","charged_credits":812,"output":{"output":"{...}"}}
import urllib.request, json
req = urllib.request.Request(
BASE + "/run-stream",
data=json.dumps(payload).encode(),
headers={
"Authorization": "Bearer " + token,
"Content-Type": "application/json",
"Idempotency-Key": "semgrep-desk:audit:f2834bd4:a1",
},
)
body = ""
with urllib.request.urlopen(req) as r:
event = None
for raw in r:
line = raw.decode().rstrip("\n")
if line.startswith("event: "):
event = line[7:]
elif line.startswith("data: "):
data = json.loads(line[6:])
if event == "delta":
body += data["text"]
elif event == "done":
body = data["output"]["output"]
print(json.loads(body)["verdict"])
const res = await fetch(BASE + "/run-stream", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
"Idempotency-Key": "semgrep-desk:audit:f2834bd4:a1",
},
body: JSON.stringify(payload),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let body = "";
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let i;
while ((i = buffer.indexOf("\n\n")) >= 0) {
const frame = buffer.slice(0, i);
buffer = buffer.slice(i + 2);
const ev = /^event: (.+)$/m.exec(frame)?.[1];
const dt = /^data: (.+)$/m.exec(frame)?.[1];
if (!dt) continue;
const parsed = JSON.parse(dt);
if (ev === "delta") body += parsed.text;
if (ev === "done") body = parsed.output.output;
}
}
console.log(JSON.parse(body).verdict);
req, _ := http.NewRequest("POST", base+"/run-stream", bytes.NewReader(payloadBytes))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "semgrep-desk:audit:f2834bd4:a1")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
sc := bufio.NewScanner(res.Body)
sc.Buffer(make([]byte, 1<<20), 1<<20)
var event, body string
for sc.Scan() {
line := sc.Text()
switch {
case strings.HasPrefix(line, "event: "):
event = strings.TrimPrefix(line, "event: ")
case strings.HasPrefix(line, "data: "):
var d struct {
Text string `json:"text"`
Output struct{ Output string } `json:"output"`
}
json.Unmarshal([]byte(strings.TrimPrefix(line, "data: ")), &d)
if event == "delta" {
body += d.Text
} else if event == "done" {
body = d.Output.Output
}
}
}
fmt.Println(body)
HttpRequest req = HttpRequest.newBuilder(URI.create(BASE + "/run-stream"))
.header("Authorization", "Bearer " + token)
.header("Content-Type", "application/json")
.header("Idempotency-Key", "semgrep-desk:audit:f2834bd4:a1")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
StringBuilder body = new StringBuilder();
String[] event = { "" };
HTTP.send(req, HttpResponse.BodyHandlers.ofLines()).body().forEach(line -> {
if (line.startsWith("event: ")) event[0] = line.substring(7);
else if (line.startsWith("data: ") && event[0].equals("delta"))
body.append(line.substring(6));
});
System.out.println(body);
uri = URI(BASE.to_s + '/run-stream')
req = Net::HTTP::Post.new(uri,
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{token}",
'Idempotency-Key' => 'semgrep-desk:audit:f2834bd4:a1')
req.body = JSON.generate(payload)
event = nil
body = +''
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(req) do |res|
res.read_body do |chunk|
chunk.each_line do |line|
event = line[7..].strip if line.start_with?('event: ')
next unless line.start_with?('data: ')
d = JSON.parse(line[6..])
body << d['text'] if event == 'delta'
body = d['output']['output'] if event == 'done'
end
end
end
end
puts JSON.parse(body)['verdict']
$body = "";
$event = null;
$ch = curl_init($BASE . "/run-stream");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Authorization: Bearer " . $token,
"Idempotency-Key: semgrep-desk:audit:f2834bd4:a1",
],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_WRITEFUNCTION => function ($ch, $chunk) use (&$body, &$event) {
foreach (explode("\n", $chunk) as $line) {
if (str_starts_with($line, "event: ")) { $event = trim(substr($line, 7)); }
elseif (str_starts_with($line, "data: ")) {
$d = json_decode(substr($line, 6), true);
if ($event === "delta") { $body .= $d["text"]; }
if ($event === "done") { $body = $d["output"]["output"]; }
}
}
return strlen($chunk);
},
]);
curl_exec($ch);
curl_close($ch);
echo json_decode($body, true)["verdict"] . "\n";
var req = new HttpRequestMessage(HttpMethod.Post, "run-stream") {
Content = new StringContent(JsonSerializer.Serialize(payload),
Encoding.UTF8, "application/json")
};
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
req.Headers.Add("Idempotency-Key", "semgrep-desk:audit:f2834bd4:a1");
var res = await http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
using var reader = new StreamReader(await res.Content.ReadAsStreamAsync());
string ev = null, body = "";
while (await reader.ReadLineAsync() is string line) {
if (line.StartsWith("event: ")) ev = line[7..];
else if (line.StartsWith("data: ")) {
var d = JsonDocument.Parse(line[6..]).RootElement;
if (ev == "delta") body += d.GetProperty("text").GetString();
if (ev == "done") body = d.GetProperty("output").GetProperty("output").GetString();
}
}
Console.WriteLine(JsonDocument.Parse(body).RootElement.GetProperty("verdict"));
The output contract
The reply is one JSON object. It is identical in shape across all four lanes; only the inner meaning of steps, detail_table and artifact changes.
| Field | Type | Meaning |
|---|---|---|
lane | string | The lane that was answered. |
title | string | Short title naming the rule under review. |
headline | string | One sentence: the single most important thing. |
verdict | string | ship, tighten or rewrite. Any other value is coerced to tighten. |
confidence | string | high, medium or low. |
summary | string | Two to four sentences of prose. |
findings[] | array | id, severity (critical/high/medium/low), rule, operator, problem, evidence, fix. An entry with no problem is dropped by the renderer. |
steps[] | array | n, phase, action, owner, signal. An entry with no action is dropped. Renumbered from 1. |
detail_table | object | title, columns[], rows[][]. Rows are padded or truncated to the column count. The columns differ per lane — see the worked examples below. |
artifact | object | title, filename, language (yaml or markdown), content. For the three YAML lanes the content is a complete loadable rule file starting with rules:. |
coverage_check[] | array | flag, status (confirmed/cleared/not-applicable), note. One entry per prescan flag id you sent. |
assumptions[] | array | Strings. May be empty. |
open_questions[] | array | Strings. May be empty. |
One worked example per lane
The only field that changes between these is task. What comes back differs in the three per-lane sections below.
Rule audit task: "audit"
The operator inventory, the findings, the ordered edit plan and the corrected rule.
detail_table.columnsis["Operator", "Polarity", "What it matches", "Concern"]artifact.filenameisrule.yaml,artifact.languageisyaml
curl -sS -X POST https://api.skillsafe.ai/v1/app-api/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: semgrep-desk:audit:f2834bd4:a1" \
-d '{"task": "audit", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}'
payload = {
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
job = post("/run", payload, token, headers={"Idempotency-Key": "semgrep-desk:audit:f2834bd4:a1"})
print(job)
const payload = {
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
};
const job = await call("/run", { body: payload, token, headers: { "Idempotency-Key": "semgrep-desk:audit:f2834bd4:a1" } });
console.log(job);
var payload map[string]any
json.Unmarshal([]byte(`{"task": "audit", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}`), &payload)
job, _ := call("/run", token, payload, map[string]string{"Idempotency-Key": "semgrep-desk:audit:f2834bd4:a1"})
fmt.Println(job)
String payload = """
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""";
System.out.println(post("/run", payload, token));
payload = JSON.parse(<<~'JSON')
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON
job = post('/run', payload, token, { 'Idempotency-Key' => 'semgrep-desk:audit:f2834bd4:a1' })
puts job
$payload = json_decode(<<<'JSON'
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON, true);
$job = call_api("/run", $payload, $token, ["Idempotency-Key: semgrep-desk:audit:f2834bd4:a1"]);
print_r($job);
var payload = JsonSerializer.Deserialize<object>("""
{
"task": "audit",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""");
var job = await Call("run", payload, token, ("Idempotency-Key", "semgrep-desk:audit:f2834bd4:a1"));
Console.WriteLine(job);
False-positive triage task: "fp-triage"
A verdict per annotated line, and the narrowest exclusions applied to a whole rule file.
detail_table.columnsis["Line", "Annotation", "Expected", "Rule as written", "Verdict"]artifact.filenameisrule-triaged.yaml,artifact.languageisyaml
curl -sS -X POST https://api.skillsafe.ai/v1/app-api/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: semgrep-desk:fp-triage:f2834bd4:a1" \
-d '{"task": "fp-triage", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}'
payload = {
"task": "fp-triage",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
job = post("/run", payload, token, headers={"Idempotency-Key": "semgrep-desk:fp-triage:f2834bd4:a1"})
print(job)
const payload = {
"task": "fp-triage",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
};
const job = await call("/run", { body: payload, token, headers: { "Idempotency-Key": "semgrep-desk:fp-triage:f2834bd4:a1" } });
console.log(job);
var payload map[string]any
json.Unmarshal([]byte(`{"task": "fp-triage", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}`), &payload)
job, _ := call("/run", token, payload, map[string]string{"Idempotency-Key": "semgrep-desk:fp-triage:f2834bd4:a1"})
fmt.Println(job)
String payload = """
{
"task": "fp-triage",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""";
System.out.println(post("/run", payload, token));
payload = JSON.parse(<<~'JSON')
{
"task": "fp-triage",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON
job = post('/run', payload, token, { 'Idempotency-Key' => 'semgrep-desk:fp-triage:f2834bd4:a1' })
puts job
$payload = json_decode(<<<'JSON'
{
"task": "fp-triage",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON, true);
$job = call_api("/run", $payload, $token, ["Idempotency-Key: semgrep-desk:fp-triage:f2834bd4:a1"]);
print_r($job);
var payload = JsonSerializer.Deserialize<object>("""
{
"task": "fp-triage",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""");
var job = await Call("run", payload, token, ("Idempotency-Key", "semgrep-desk:fp-triage:f2834bd4:a1"));
Console.WriteLine(job);
Bypass variants task: "variants"
The vectors the rule walks past, with honest caught-now and caught-after columns.
detail_table.columnsis["Vector", "Example", "Caught now", "Caught after"]artifact.filenameisrule-variants.yaml,artifact.languageisyaml
curl -sS -X POST https://api.skillsafe.ai/v1/app-api/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: semgrep-desk:variants:f2834bd4:a1" \
-d '{"task": "variants", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}'
payload = {
"task": "variants",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
job = post("/run", payload, token, headers={"Idempotency-Key": "semgrep-desk:variants:f2834bd4:a1"})
print(job)
const payload = {
"task": "variants",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
};
const job = await call("/run", { body: payload, token, headers: { "Idempotency-Key": "semgrep-desk:variants:f2834bd4:a1" } });
console.log(job);
var payload map[string]any
json.Unmarshal([]byte(`{"task": "variants", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}`), &payload)
job, _ := call("/run", token, payload, map[string]string{"Idempotency-Key": "semgrep-desk:variants:f2834bd4:a1"})
fmt.Println(job)
String payload = """
{
"task": "variants",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""";
System.out.println(post("/run", payload, token));
payload = JSON.parse(<<~'JSON')
{
"task": "variants",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON
job = post('/run', payload, token, { 'Idempotency-Key' => 'semgrep-desk:variants:f2834bd4:a1' })
puts job
$payload = json_decode(<<<'JSON'
{
"task": "variants",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON, true);
$job = call_api("/run", $payload, $token, ["Idempotency-Key: semgrep-desk:variants:f2834bd4:a1"]);
print_r($job);
var payload = JsonSerializer.Deserialize<object>("""
{
"task": "variants",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""");
var job = await Call("run", payload, token, ("Idempotency-Key", "semgrep-desk:variants:f2834bd4:a1"));
Console.WriteLine(job);
Variant hunt task: "hunt"
Search targets with real queries, and a Markdown plan a person executes against the repository.
detail_table.columnsis["Target", "Where", "Query", "Why it is a likely site"]artifact.filenameisvariant-hunt.md,artifact.languageismarkdown
curl -sS -X POST https://api.skillsafe.ai/v1/app-api/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: semgrep-desk:hunt:f2834bd4:a1" \
-d '{"task": "hunt", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}'
payload = {
"task": "hunt",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
job = post("/run", payload, token, headers={"Idempotency-Key": "semgrep-desk:hunt:f2834bd4:a1"})
print(job)
const payload = {
"task": "hunt",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
};
const job = await call("/run", { body: payload, token, headers: { "Idempotency-Key": "semgrep-desk:hunt:f2834bd4:a1" } });
console.log(job);
var payload map[string]any
json.Unmarshal([]byte(`{"task": "hunt", "rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n", "sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n", "intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.", "target": "pr-blocking", "tolerance": "noise-averse", "context": "Flask service, ~40k lines of Python.", "prescan_facts": {"risk_tier": "low", "flags": [{"id": "SL-META-THIN", "severity": "low", "rule": "python-yaml-unsafe-load", "detail": "metadata is missing 6 of the conventional keys"}], "rules": [{"id": "python-yaml-unsafe-load", "languages": ["python"], "severity": "ERROR", "mode": "search", "operators": 4, "depth": 1, "binds": ["$X"], "breadth": "moderate", "breadth_score": 7}], "annotations": [{"kind": "ruleid", "id": "python-yaml-unsafe-load", "line": 1}, {"kind": "ok", "id": "python-yaml-unsafe-load", "line": 3}]}}`), &payload)
job, _ := call("/run", token, payload, map[string]string{"Idempotency-Key": "semgrep-desk:hunt:f2834bd4:a1"})
fmt.Println(job)
String payload = """
{
"task": "hunt",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""";
System.out.println(post("/run", payload, token));
payload = JSON.parse(<<~'JSON')
{
"task": "hunt",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON
job = post('/run', payload, token, { 'Idempotency-Key' => 'semgrep-desk:hunt:f2834bd4:a1' })
puts job
$payload = json_decode(<<<'JSON'
{
"task": "hunt",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
JSON, true);
$job = call_api("/run", $payload, $token, ["Idempotency-Key: semgrep-desk:hunt:f2834bd4:a1"]);
print_r($job);
var payload = JsonSerializer.Deserialize<object>("""
{
"task": "hunt",
"rule": "rules:\n - id: python-yaml-unsafe-load\n languages: [python]\n severity: ERROR\n message: yaml.load($X) without a safe loader builds arbitrary objects from $X.\n patterns:\n - pattern: yaml.load($X)\n - pattern-not: yaml.load($X, Loader=yaml.SafeLoader)\n - metavariable-regex:\n metavariable: $X\n regex: (?i)(request|argv)\n",
"sample": "# ruleid: python-yaml-unsafe-load\nyaml.load(request.data)\n# ok: python-yaml-unsafe-load\nyaml.safe_load(request.data)\n",
"intent": "Flag anywhere a request-derived value is parsed by yaml.load without a safe loader.",
"target": "pr-blocking",
"tolerance": "noise-averse",
"context": "Flask service, ~40k lines of Python.",
"prescan_facts": {
"risk_tier": "low",
"flags": [
{
"id": "SL-META-THIN",
"severity": "low",
"rule": "python-yaml-unsafe-load",
"detail": "metadata is missing 6 of the conventional keys"
}
],
"rules": [
{
"id": "python-yaml-unsafe-load",
"languages": [
"python"
],
"severity": "ERROR",
"mode": "search",
"operators": 4,
"depth": 1,
"binds": [
"$X"
],
"breadth": "moderate",
"breadth_score": 7
}
],
"annotations": [
{
"kind": "ruleid",
"id": "python-yaml-unsafe-load",
"line": 1
},
{
"kind": "ok",
"id": "python-yaml-unsafe-load",
"line": 3
}
]
}
}
""");
var job = await Call("run", payload, token, ("Idempotency-Key", "semgrep-desk:hunt:f2834bd4:a1"));
Console.WriteLine(job);
Chaining lanes
The web page offers each lane's result to the next as a button. From your own code, put a digest of the previous reply in upstream and, when that lane produced a loadable rule file, send artifact.content as the next call's rule. The pipeline is audit → fp-triage → variants → hunt.
What this API will not do
It does not execute Semgrep, does not read your repository, and does not verify that a generated rule matches anything. It reads the text you send. Every generated YAML artifact should be run through semgrep --validate and your own test corpus before it is merged - the web page re-parses each artifact and warns when it would not load, and your client should do the same.