Skip to content
← Blog & Education · compliance 13 min read

API security posture — what your OpenAPI spec can prove, and what it cannot

Import an OpenAPI spec to build an endpoint inventory, read an automated OWASP API Top 10 position over what the spec declares, record the manual attestation beside it on a cadence that chases its owner, and see where spec and inventory have drifted — with a scale that refuses to say 'good'.

By The Talarity team · September 23, 2026

The APIs you expose are the part of your estate an attacker reaches first and your governance reaches last. Code gets scanned, dependencies get an SBOM, pipelines get attested — and the API surface is usually a spec file in a repository that nobody has read against a security question.

Talarity’s API Inventory takes that spec seriously as evidence, and is equally clear about where it stops being evidence. It gives you two OWASP API Top 10 pictures, deliberately kept apart: one the product computes from what your spec declares, and one a person asserts. Neither is allowed to stand in for the other, and only one of them becomes audit evidence.

Step 1 — The register, and what the cards are counting

The register opens on every API you have access to, with the summary cards above it and the list below.

The distinction those two halves draw is what makes the page readable: the cards count the whole inventory; the filters below narrow only the list, and the page says so in its own words rather than leaving you to discover it. A page that quietly recomputes its headline figures every time someone types in a search box teaches its readers to distrust both.

Six actions sit on the header — Refresh, Attestation cadence, Import spec, Export CSV, Export endpoints and Add API. Add API is the hand-registration path for a service you know about before anyone has published a spec for it: it takes a name, an exposure, a data classification and an owner, and seeds the ten OWASP rows unassessed, exactly as an import does.

The API inventory register: a banner reading that four APIs are due for their OWASP posture attestation with a Show them action, the page header carrying Refresh, Attestation cadence, Import spec, Export CSV, Export endpoints and Add API, then eleven summary cards — five APIs in service, four posture attestations due with a sub-line reading four never attested, zero overdue, ninety-day cadence, sixteen endpoints, one public API, endpoint auth at 76.9 per cent with three endpoints of unknown auth, OWASP risks assessed at 16.7 per cent, assessed-to-addressed at 75 per cent, two at-risk OWASP categories, forty unassessed, zero APIs with a stored grade at risk and four with no stored grade — above the note that these cards count the whole inventory you have access to while the filters below narrow the list only, and beneath it the search box, exposure, attestation-state and sort selects, an include-archived checkbox, the line reading Showing 1 to 5 of 5 APIs, and the selection bar reading None selected.

Step 2 — Import the spec, get the endpoints

Import spec takes an OpenAPI 3.0 or 3.1 document, pasted or chosen from disk, up to 8MB. The import upserts the API, batch-inserts its endpoints, and seeds the ten OWASP API Top 10 rows at unassessed — so the manual attestation starts as an honest blank rather than an implied pass. Re-importing the same document is safe: it updates rather than duplicates.

A specification declaring no paths at all is refused, with the reason said out loud. That is not pedantry. A well-formed spec with an empty paths object imports silently as “an API with nothing in it”, and an API with no endpoints grades insufficient_data — a real state that would then be indistinguishable from a failed import. Operations the parser cannot use are reported back as a skipped-entry notice on the API rather than dropped quietly, for the same reason.

The Import OpenAPI specification dialog open over the register, scrolled to its foot: an optional API name field defaulting to the spec title, an optional repository picker with a provider filter and a search box, exposure and data-classification selects both reading Unknown, a required OpenAPI specification text area labelled JSON or YAML holding an example document, a Choose a file button beside the words "or paste the specification above. Up to 8 MB.", and below them the sentence explaining that each operation becomes an endpoint, that authentication is read exactly as the spec declares it — an operation the spec leaves silent on shows as "unknown" — and that re-importing the same spec is safe, with Cancel and Import in the footer.

Step 3 — Read the endpoint table, and the three auth states

The endpoint table lists every path and method the spec declared, with the authentication position for each. It has three states, and the difference between two of them is the whole point.

  • Required — the spec declares a security scheme for that operation.
  • None — the spec explicitly declares the operation open. That is a decision somebody made and wrote down.
  • Unknown — the spec declared no security at all for that operation. Not “we could not tell”, and not “unprotected”: an absence of documentation.

Unknown is deliberately never rendered as insecure, because an endpoint may well be protected by a gateway the spec says nothing about. What the page will not do is guess in the direction that looks better or the direction that looks worse, and it does not quietly fold the unknowns in either: the Endpoint auth card on the register counts Required against Required + None and reports the unknowns beside it as their own number, because a percentage over endpoints nobody has classified would be a made-up one.

All three travel into the CSV as three states, for the same reason — rendering the unknown as “None” in an export would turn an absence of information into a finding, in the artefact with the longest half-life.

The endpoints table for an API whose specification declares no security for any operation, clipped to the table itself: a Method, Path, Auth and Source column, and three GET rows — a reports collection, a single report by id, and a report export marked deprecated — each carrying an Unknown chip in the Auth column, drawn as a dashed neutral outline rather than in a warning colour, with Specification as the source of all three.

Step 4 — The automated Top 10, and the grade that does not exist

The engine evaluates the ten OWASP API Top 10 (2023) categories against the metadata your spec actually declares, and returns a verdict per category — including not assessable, with the reason stated.

That state is not a hedge. Four of the ten categories cannot be judged from a spec at all:

CategoryWhy the spec cannot answer it
API3 Broken object property level authorizationneeds request and response schemas, which the inventory does not store
API4 Unrestricted resource consumptionneeds rate limits, pagination bounds and payload caps
API6 Unrestricted access to sensitive business flowsneeds business-flow annotations and anti-automation signals
API10 Unsafe consumption of APIsis about the APIs you consume, which this inventory does not track

So the assessable ceiling is six, not ten.

The grade scale is a closed set of three, and the best one is “Needs review”

needs_review · at_risk · insufficient_data. There is deliberately no “good”.

That is not modesty, it is arithmetic. Of the six assessable categories, only three — API2, API8 and API9 — have a passing branch at all. API1, API5 and API7 top out at warn by design: object ownership, role-to-function segregation and SSRF parameter semantics are not observable in metadata, so the rule may say “authentication is present, ownership is unproven” and must never say “this is safe”.

A “good” grade required no fails, no warns, and at least five categories assessed. With no warns and no fails, assessed equals pass — and pass tops out at three, so the condition could not be met. An exhaustive sweep of the engine’s input space confirms it rather than assuming it: over every combination of paths, methods, authentication positions, exposures, classifications and spec states, the highest number of passes ever observed is three, the highest number of categories ever assessed is six, and the “good” condition is satisfied by nothing at all.

The tempting fix is to lower the threshold until “good” appears. That would have been the wrong one: it would manufacture exactly the all-clear the engine was correctly declining to give — three hygiene checks passed and seven categories unknown, reported as fine. The grade was removed from the vocabulary instead, and needs_review is the ceiling, which is the honest summary of what a spec file can prove about API security.

A fail or a warn, by contrast, is a real observed problem and is never suppressed behind a coverage floor. The engine is permitted to find things; it is not permitted to clear you.

The automated verdict list for one API's OWASP API Security categories, clipped to the first five of ten: API1:2023 Broken Object Level Authorization marked Warn, saying five object-referencing endpoints were examined and each requires authentication but object-ownership enforcement is not observable in declared metadata, with those five endpoints listed beneath it; API2:2023 Broken Authentication marked Pass; API3:2023 Broken Object Property Level Authorization and API4:2023 Unrestricted Resource Consumption both marked Not assessable, each stating that the property schemas and the resource-consumption controls it would need are not captured in the inventory; and API5:2023 Broken Function Level Authorization marked Warn, naming the single administrative endpoint it examined.

Step 5 — Recompute to persist the position

The automated grade is computed on demand; Recompute persists a snapshot. That matters because an auditor’s question is usually historical — what did this look like at the end of the quarter? — and a figure recomputed live cannot answer it. The snapshots are appended, not overwritten, and the panel head reports the most recent one with its date and the person who ran it. When the endpoints have changed since that snapshot was taken, the row carries a grade out of date marker rather than letting a stale grade pass as current.

The head is also where the article’s arithmetic meets the product: it states the share of categories that can be assessed at all, and the pass, warn, fail and not-assessable counts behind the grade.

The automated posture panel's header after Recompute, clipped to the header: a live grade of Needs review, the line "60% of categories assessable · 3 pass, 3 warn, 0 fail, 4 not assessable", the last persisted snapshot with its date, its grade and the name of the person who ran it, and the Recompute control beside them.

Step 6 — Read the drift, in both directions

Spec-vs-inventory drift is reported as two distinct buckets, because they are two different problems:

  • Undocumented — endpoints this inventory tracks that the imported specification does not declare. Something is being tracked that your published documentation does not describe.
  • Removed from the latest import — endpoints in this inventory that the most recent import no longer declares. Either they are genuinely gone, or the spec is ahead of the deployment. Each row carries a Remove action, because retiring the endpoint is the one remedy that bucket has.

Collapsing those into one “drift” number would have made both unactionable. Each bucket renders its own count, and a bucket at zero says so in its own words rather than leaving you to infer it from silence.

What the page does not claim is live-traffic discovery. Comparing the spec against endpoints actually serving traffic needs a gateway integration this inventory does not have, and the page says so plainly rather than presenting an empty bucket as an all-clear.

The spec-versus-inventory drift section: a heading, the sentence that this compares documentation and does not observe live traffic, an Undocumented bucket showing zero with a Nothing to report all-clear reading "Every endpoint this inventory tracks also appears in the imported specification", a Removed from the latest import bucket showing one with its own explanatory line and a table row for a POST refunds operation, the line "10 endpoints compared.", and the disclosure that endpoints seen in live API traffic but never declared cannot be detected without an API-gateway traffic connector.

Step 7 — The endpoint the spec never declared

The Undocumented bucket exists because an endpoint can enter this inventory without coming from a specification. Add endpoint records one by hand, and it is stored with its provenance — the row remembers that a person put it there, not an import. Edit endpoint can correct the summary, the authentication position and the deprecation marker afterwards; it is the way a mis-declared auth flag gets fixed, which matters because that flag is what Step 4 grades.

What edit will not change is the provenance. A surface that let you re-label a hand-entered endpoint as “specified” would let a drift bucket be emptied by relabelling rather than by publishing, and the dialog refuses it by name rather than ignoring it.

It is also the honest half of a feature that could have been dishonest. A drift bucket with no way to fill it is a heading that is always empty and always reads as an all-clear — which is the shape this page spent a review round removing everywhere else.

The Add endpoint dialog open over an API's detail: a required-field legend, a Method select, a required Path field, an Authentication select offering Not known, Required and None, an optional summary, and a checkbox marking the operation deprecated, with Cancel and Add endpoint in the footer.

Step 8 — The manual attestation, and why it is kept separate

Beside the automated panel sits the manual OWASP attestation: the ten categories, each marked by a person as addressed, at risk, not applicable or unassessed, with a note, and each carrying the date and name of whoever last attested it.

These two pictures are never merged, and that is the design. The automated verdicts are what your spec declares; the attestation is what your team asserts after looking. A single blended score would let a clean spec paper over an unexamined category, which is precisely the failure the split exists to prevent.

Two cards report it, and the split between them is the point:

  • OWASP risks assessed — how many applicable categories you have looked at at all. An unassessed category counts against you here. Silence is not a pass. Categories you have marked not applicable leave the denominator, because a category you have judged inapplicable is one you have looked at.
  • Assessed → addressed — of the ones you did assess, how many came out addressed — and it says so in its own subtitle, because a high number there means nothing without the first card beside it.

An earlier single “coverage” percentage computed addressed over addressed-plus-at-risk, which quietly dropped unassessed categories from the denominator and could show a confident 100% to a team that had assessed one category out of ten.

An archived API is refused an attestation outright. It has left every coverage denominator and the reminder sweep by the product’s own definition, so a posture asserted against it would be an assertion about something the product says is not in service.

The matrix is floored at 700px inside a detail pane that is narrower than that on a 1440-wide screen, so it scrolls horizontally rather than compressing: ten rows of a legally meaningful record must stay reachable at any pane width. The frame below ends at the Posture column for that reason — the notes and the last-attested column sit to the right of it.

The manual OWASP attestation matrix for an API, clipped to the categories and the posture: a Risk column of OWASP identifiers, a Category · last attested column carrying each category's name and the line "Attested Sep 22, 2026 · Rowan Whitfield", and a Posture select per row reading Addressed, Addressed, At risk, Addressed, At risk and Not applicable down the six rows in frame.

Step 9 — The history, and the evidence a save writes

The posture table is overwritten on every save — it holds the current assertion. So a save that asserts something also writes one append-only evidence row carrying the full posture as asserted, with its provenance edge back to the API, and the history below the editor lists them newest first with a count.

That is the difference between a control picture and audit evidence. Without it, last quarter’s attestation survives nowhere an auditor can open; the only artefact is the current state, which is precisely the state the auditor is not asking about.

A save that asserts nothing writes no evidence row. Ten “not assessed” lines dated today would be a record of an event that did not happen.

The attestation history panel for an API, clipped to the history: the heading "Attestation history (11)" and five dated rows, each giving the date and time the attestation was recorded, the name of the person who recorded it, and the posture they asserted — six addressed, two at risk, two not applicable, zero unassessed.

Step 10 — How often it has to be said again

An attestation with no expiry is a one-off. Attestation cadence sets how often an API’s manual OWASP posture must be re-asserted, org-wide: a platform default of 90 days, overridable to any whole number of days from 1 to 3,650, and cleared back to the default by emptying the box and saving.

The dialog distinguishes using the platform default from your organisation has set, and it states the reminder ladder it drives rather than leaving you to discover it. A reader who cannot change the cadence can still open it and see it — an API owner needs to know the cadence they are held to whether or not they set it.

The OWASP posture attestation cadence dialog: a status line reading "Using the platform default of 90 days.", a numeric field labelled "Re-attest each API's OWASP posture every (days)" holding 90, and beneath it the sentence stating that reminders go to each API's owner 30, 14, 7 and 1 days before the attestation falls due and 7, 30 and 90 days after, that an overdue or never-attested API with an owner also gets one work item, and that clearing the box and saving returns to the platform default — with Cancel and Save cadence in the footer.

Step 11 — Who gets chased, and by what

A cadence that nothing enforces is a number in a settings dialog. This one drives four things, in order of how insistent they are:

  1. The filter and the card. The register narrows to due, overdue, never attested or current, and the “Posture attestations due” card carries the never-attested and overdue counts with the cadence beside them.
  2. The nav badge, so the obligation is visible without opening the page.
  3. An in-app notification to the API’s owner, raised by a daily sweep at the points the cadence dialog names — 30, 14, 7 and 1 days before it falls due, then 7, 30 and 90 days after. An API that has never been attested is chased on how long it has been in the inventory instead, because there is no previous attestation to count an interval from.
  4. A work item, but only for an obligation actually missed — overdue, or never attested — and only where there is an owner to give it to. Attesting closes it; archiving the API cancels it.

All four need an owner, which is set on the API’s Edit dialog or handed out with Assign attestation — singly from the detail pane, or across a selection from the bulk bar. APIs that are due with no owner are counted and reported by the sweep rather than silently skipped, and the API’s own detail says so in as many words: no owner is named, so nobody is reminded when this falls due. A reminder system that drops the unassigned work is a reminder system that reports success.

The register filtered to the APIs whose posture attestation is due: the summary cards above still counting the whole inventory of five, the attestation-state select reading "Due for attestation", the line "Showing 1–4 of 4 APIs", and the narrowed list beneath it — an internal API and an API with no paths, each with its exposure chip, a Top 10 chip reading Not scored, its endpoint count, "Never attested", a red posture-never-attested chip and its ten-cell OWASP coverage bar.

Link finding attaches a finding from the shared AppSec register to this API. The picker searches by title, package or file path, and opens filtered to the api finding class — but “All classes” is one selection away, because a container or secret finding can legitimately belong to an API and the backend imposes no class restriction. An optional note records why the finding relates to this API, which is the part worth having six months later.

An org with no findings at all is not left staring at an empty box: the picker says so and offers a link to the findings register, and a failed search says something different again, with a Retry. The two are distinguishable, which is the difference between a dead end and a starting point.

The Link finding to API dialog: a required Finding field with a class select reading API, a search box holding the word "intake", and beneath it the matching finding — "Partner event intake is reachable without an API key" with a Critical severity chip, a status of Open and a class of API — beside an optional note field placeheld "Why this finding relates to the API", with Cancel and Link finding in the footer.

Step 13 — …and the edge is readable from both ends

A link you can only see from one end is half a link. The API’s detail lists the findings attached to it, with the severity and status they carry in the register and a deep link into it; the finding lists the APIs it affects. The same edge, read from whichever end you arrived at.

That matters because the two ends are reached by different people. Whoever triages the findings register is not usually whoever owns the API, and an edge visible only to the person who created it is an edge the other one will not know about. The link is removable from the API’s side, and an API is reachable from the app’s global search, so an inventory entry is not something you have to be on this page to find.

Beneath the findings sits Related items, which does the same job for the rest of the estate: the risks, controls, policies and incidents an API bears on, listed in both directions — what this API points at, and what points at it.

The linked findings table on an API's detail, clipped to the table: Finding, Class, Severity and Status columns over one row — the partner event intake finding as a link into the findings register, a class of API, a Critical severity chip and a status of Open.

Step 14 — Retire what is no longer in service, and get the data out

Two things every inventory needs and most demos skip:

  • Archive retires an API that is real but decommissioned — singly, or for a whole selection in one call rather than a loop. Archived APIs keep their endpoints, attestations, Top 10 snapshot and linked findings, and leave every coverage denominator, the reminder sweep and the badge. Without it, a service you turned off two years ago drags your posture figures down forever. Restore brings it back, and the register discloses how many archived APIs the default view is hiding, so a count never quietly reads as the whole history. Delete exists for the other case — an API nothing has ever been asserted about, usually a typo in a spec’s title — and it refuses anything else up front, naming Archive instead rather than inviting a press it already knows it will reject.
  • Export produces two CSVs, because one row per API is not the inventory. Export CSV is the register — name, exposure, owner, last attested, grade and the OWASP rollup. Export endpoints is every operation of every API in scope, with the auth, deprecation and provenance metadata the rules actually grade. Both reuse the register’s own predicate rather than re-spelling it, so an export can never show you a set the page would not, and both carry the scope disclosures into the file.

The list itself is built for more than a demo’s worth: search, an exposure filter, an attestation-state filter, a sort that orders the whole inventory server-side rather than the page you are holding, and a pager that says which slice you are on.

The register with two rows selected: the bulk action bar reporting "2 selected" with a Clear action and the verbs the selection can actually take — Assign attestation and Archive 2 — above the list it will act on, where the two chosen APIs carry a selected outline and every row shows its exposure chip, Top 10 grade chip, endpoint count, last-attested line, posture chips and ten-cell OWASP coverage bar.

Where this page stops, on purpose

  • Triaging findings is the findings register. This page links them; it does not work them.
  • The dashboard’s API section summarises this page — reading the AppSec dashboard covers that view; this article is the one that goes deeper.
  • Live-traffic discovery is not here. Comparing your spec against what is actually serving needs a gateway integration, and the page says so rather than leaving an empty bucket to be read as an all-clear.

What you walk away with

An API surface under the same governance as the rest of your code: every endpoint inventoried from the spec you already publish, plus the ones you record by hand; an automated OWASP position over what that spec actually declares, persisted as dated snapshots rather than recomputed and lost; a manual attestation recorded beside it rather than blended into it, on a cadence that chases its owner and writes evidence an auditor can open; drift reported in both directions with a remedy on the row; findings and related records linked from the shared registers and visible from both ends; and a retirement path and two exports, so the numbers describe the services you actually run and can leave the product when somebody asks for them.

And a grading scale that will not tell you that you are fine. Needs review is the best grade this engine can give, because six of the ten categories are the most a spec can speak to and only three of those can ever pass. A tool that offered you “good” on that evidence would be telling you something it cannot know.

Loading…

Keep reading

See Talarity in action.

A 30-minute walkthrough or 60 days of early access — your call.