<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Cory Sorel</title>
    <description>The latest articles on DEV Community by Cory Sorel (@uncommoncarp).</description>
    <link>https://dev.to/uncommoncarp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3920420%2F01a4e546-f856-4f96-a2df-af35105a212f.jpeg</url>
      <title>DEV Community: Cory Sorel</title>
      <link>https://dev.to/uncommoncarp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/uncommoncarp"/>
    <language>en</language>
    <item>
      <title>I Built An API Security Scanner That Won't Shoot You in the Foot</title>
      <dc:creator>Cory Sorel</dc:creator>
      <pubDate>Mon, 22 Jun 2026 16:00:00 +0000</pubDate>
      <link>https://dev.to/uncommoncarp/i-built-an-api-security-scanner-that-wont-shoot-you-in-the-foot-28oa</link>
      <guid>https://dev.to/uncommoncarp/i-built-an-api-security-scanner-that-wont-shoot-you-in-the-foot-28oa</guid>
      <description>&lt;p&gt;Four years into a career focused on APIs and developer tooling, I kept running into the same gap: security scanning options were either too heavy (stand up a platform, configure a workspace, wait for results) or too risky to run in CI without careful guardrails. I wanted a lightweight, CLI-first tool that gave a clear first pass without the danger of destructive side effects.&lt;/p&gt;

&lt;p&gt;That's Sentinel: a spec-aware API security scanner built for developers and CI pipelines. Out of the box it sends only GETs, keeps burst probes conservative, and requires an explicit opt-in, plus an OpenAPI spec, before it touches anything injection-related. Safety isn't a feature, it's the default.&lt;/p&gt;

&lt;p&gt;Here's how that show's up in the implementation:&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Spec first endpoint selection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When an OpenAPI spec is provided, Sentinel resolves the full set of endpoints to test once, before any suite runs, and passes them down via &lt;code&gt;ctx.selectedEndpoints&lt;/code&gt;. Suites call &lt;code&gt;resolveEndpoints(ctx.selectedEndpoints)&lt;/code&gt; to get that list, it's never re-computed. The spec's &lt;code&gt;servers[0].url&lt;/code&gt; base path is extracted and normalized into every endpoint path at selection time, so suites never have to think about it.&lt;/p&gt;

&lt;p&gt;The fallback is deliberate too: no spec, no scope config, or filters that exclude everything all collapse to GET /. Suites will always get a valid list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding metadata as a manifest&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the headers suite, finding definitions live in a static REQUIRED_HEADERS array: each entry carries the finding ID, title, severity, description, remediation, and OWASP reference. The detection logic is a separate loop that iterates that manifest and checks for each header. Adding a new required header check means adding one entry to the array; there's no detection logic to write.&lt;/p&gt;

&lt;p&gt;This pattern keeps two concerns from drifting apart over time: what a finding is versus what triggers it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Errors aren't findings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RunResult has three distinct fields: &lt;code&gt;findings&lt;/code&gt;, &lt;code&gt;suiteErrors&lt;/code&gt;, and &lt;code&gt;reporterErrors&lt;/code&gt;. That separation is enforced at the type level: &lt;code&gt;suiteErrors&lt;/code&gt; and &lt;code&gt;reporterErrors&lt;/code&gt; are required fields, not optional. TypeScript won't let you construct a RunResult without accounting for both. The intent is that if a suite throws unexpectedly mid-scan, that failure surfaces in its own field with its own exit code (1) rather than being folded into security findings or silently dropped.&lt;/p&gt;

&lt;p&gt;A finding has severity, remediation, affected endpoints, evidence. An operational error has none of that. Conflating them would make the output untrustworthy in exactly the situation where you need to trust it most: when something goes wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Targeted Testing
&lt;/h3&gt;

&lt;p&gt;During the process of building Sentinel I discovered I needed a way to guarantee findings. I built Anemone: a small, configurable, vulnerable API that became a permanent part of Sentinel's testing story. It hosts a series of endpoints designed to trigger at least one finding from every suite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Headers&lt;/strong&gt;: security headers absent by default; triggers &lt;code&gt;missing_hsts&lt;/code&gt;, &lt;code&gt;missing_xcto&lt;/code&gt;, &lt;code&gt;missing_referrer_policy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CORS&lt;/strong&gt;: reflects arbitrary origins with credentials by default; a second mode tests wildcard + credentials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth&lt;/strong&gt;: /api/v2/auth returns a JWT with &lt;code&gt;alg:none&lt;/code&gt;, a ~27h TTL, and no enforcement on protected routes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inventory&lt;/strong&gt;: &lt;code&gt;/debug&lt;/code&gt;, &lt;code&gt;/swagger&lt;/code&gt;, &lt;code&gt;/openapi.json&lt;/code&gt;, and &lt;code&gt;/graphql&lt;/code&gt; all exposed; &lt;code&gt;/api/v1/&lt;/code&gt; still responds alongside the declared v2 spec&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Injection&lt;/strong&gt;: &lt;code&gt;/api/v2/search&lt;/code&gt; reflects SQL errors on quote characters; &lt;code&gt;/api/v2/greet&lt;/code&gt; evaluates &lt;code&gt;{{expr}}&lt;/code&gt; in query params&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each misconfiguration is individually toggleable via env var, so you can fix one issue, re-run Sentinel, and verify that specific finding disappears. It's a useful sanity check when writing new suites.&lt;/p&gt;

&lt;p&gt;Moving Anemone from a testing fixture to a publicly hosted API required some thought, especially around the injection suite. The &lt;code&gt;{{7*7}} → 49&lt;/code&gt; behavior that triggers Sentinel's template injection probe had to work without enabling real RCE. safeEval() handles this: a hand-rolled arithmetic evaluator that resolves simple expressions and returns a fake &lt;code&gt;TemplateError&lt;/code&gt; for anything else. No &lt;code&gt;eval()&lt;/code&gt;, no execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running a demo
&lt;/h3&gt;

&lt;p&gt;You can see Sentinel in action in under a minute from your command line:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install -g @uncommon-carp/sentinel&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then point it at Anemone:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sentinel scan -u https://target.barbel.dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Sentinel outputs both a human readable Markdown doc and machine readable JSON. Here's a sample from hitting Anemone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Sentinel Report&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**Target:**&lt;/span&gt; &lt;span class="sb"&gt;`https://target.barbel.dev`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Scanned:**&lt;/span&gt; 2026-06-22T08:48:39.931Z
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Finished:**&lt;/span&gt; 2026-06-22T08:48:44.344Z
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Duration:**&lt;/span&gt; 4413ms
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Version:**&lt;/span&gt; 0.3.1

&lt;span class="gu"&gt;## Summary&lt;/span&gt;

| Severity | Count |
| --- | --- |
| Critical | 2 |
| Medium | 10 |
| Low | 6 |
| &lt;span class="gs"&gt;**Total**&lt;/span&gt; | &lt;span class="gs"&gt;**18**&lt;/span&gt; |

&lt;span class="gu"&gt;## OWASP Coverage&lt;/span&gt;

| Category | Findings |
| --- | --- |
| API2: Broken Authentication | 5 |
| API4: Unrestricted Resource Consumption | 2 |
| API8: Security Misconfiguration | 8 |
| API9: Improper Inventory Management | 3 |

&lt;span class="gu"&gt;## Findings&lt;/span&gt;
&lt;span class="p"&gt;
---
&lt;/span&gt;
&lt;span class="gu"&gt;### [Critical] JWT with alg:none detected in response&lt;/span&gt;
&lt;span class="sb"&gt;`auth.jwt_alg_none`&lt;/span&gt; | Suite: auth | API2: Broken Authentication

A JWT using the "none" algorithm was found in a response. Tokens with alg:none carry no cryptographic signature; servers that accept them can be trivially bypassed.
&lt;span class="gt"&gt;
&amp;gt; **Why it matters:** An attacker can forge arbitrary JWT claims — including elevated roles — and gain unauthorized access to any endpoint that trusts the token, with no cryptographic barrier.&lt;/span&gt;

&lt;span class="gs"&gt;**Remediation:**&lt;/span&gt; Reject JWTs with alg:none server-side and enforce an explicit algorithm allowlist.
&lt;span class="p"&gt;
---
&lt;/span&gt;
&lt;span class="gu"&gt;### [Medium] Missing Strict-Transport-Security (HSTS)&lt;/span&gt;
&lt;span class="sb"&gt;`headers.missing_hsts`&lt;/span&gt; | Suite: headers | API8: Security Misconfiguration

HSTS helps enforce HTTPS by telling browsers to only connect over TLS for a period of time.
&lt;span class="gt"&gt;
&amp;gt; **Why it matters:** Without HSTS, browsers may connect over plain HTTP on subsequent visits, enabling downgrade attacks that allow credential and session token interception on the local network.&lt;/span&gt;

&lt;span class="gs"&gt;**Remediation:**&lt;/span&gt; Add a Strict-Transport-Security header on HTTPS responses (e.g. max-age=31536000; includeSubDomains).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"startedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-22T08:45:30.542Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"targetBaseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://target.barbel.dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.3.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"finishedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-22T08:45:35.978Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5433&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"baseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://target.barbel.dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"openapi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://target.barbel.dev/openapi.json"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"findings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"headers.missing_hsts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Missing Strict-Transport-Security (HSTS)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"medium"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HSTS helps enforce HTTPS by telling browsers to only connect over TLS for a period of time."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"whyItMatters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Without HSTS, browsers may connect over plain HTTP on subsequent visits, enabling downgrade attacks that allow credential and session token interception on the local network."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remediation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Add a Strict-Transport-Security header on HTTPS responses (e.g. max-age=31536000; includeSubDomains)."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"owasp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"API8: Security Misconfiguration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"suite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"evidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"header"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"strict-transport-security"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"probed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"affected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/v2/auth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://target.barbel.dev/api/v2/auth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/v2/greet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://target.barbel.dev/api/v2/greet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/v2/users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://target.barbel.dev/api/v2/users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/v2/health"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://target.barbel.dev/api/v2/health"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/v2/search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://target.barbel.dev/api/v2/search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What's Next
&lt;/h3&gt;

&lt;p&gt;Next up: Anemone will be expanded with richer auth and rate limiting scenarios, there's deduplication work to be done for findings, and the injection suite is ready for deeper SSRF/Interactsh exploration.&lt;/p&gt;

&lt;p&gt;All it takes to get started is &lt;code&gt;npx @uncommon-carp/sentinel scan --url &amp;lt;your-api&amp;gt;&lt;/code&gt;. Stars and issues are welcome on &lt;a href="https://github.com/uncommon-carp/sentinel" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;: bug reports, false positives, or suite ideas especially.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>api</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
