← Back to API Security for AI-Assisted Development
API9:20234 min read read

Improper Inventory Management

Eliminate shadow APIs, forgotten versions, and undocumented endpoints from your attack surface.

URL: trusteed.io/academy/api-security/improper-inventory-management

Improper Inventory Management occurs when an organization loses track of which API versions, environments, and endpoints actually exist and are reachable — leaving old, unpatched versions running alongside current ones, staging or debug deployments exposed to the internet, and undocumented "shadow" endpoints that never appear in any official API specification or security review.

What Is Improper Inventory Management?

APIs accumulate surface area constantly: a v1 endpoint stays live after v2 ships because deprecating it "later" never gets prioritized; a staging environment, meant to be internal-only, gets exposed through a misconfigured load balancer; a developer adds a quick internal endpoint for testing that's never documented and never removed; a third-party integration exposes a webhook receiver that isn't tracked in the same inventory as the rest of the API surface. None of these individually look like a vulnerability — each is often functionally fine on its own — but collectively they mean security review, patching, and monitoring efforts are applied against an incomplete picture of what's actually running.

This category is distinct from having a vulnerability in a known endpoint. It's about not knowing the endpoint exists at all, which means it receives none of the scanning, patching, or monitoring applied to the inventory you do know about.

Why AI Coding Assistants Get This Wrong

AI assistants operate on the code and instructions they're given for the current task — they have no visibility into what other versions, branches, or environments of an API might already be deployed elsewhere in the organization. Left unprompted, an assistant asked to "add a new version of this endpoint" will happily create v2 without any instruction to also flag, document, or schedule deprecation of v1 — because nothing in the immediate task states that the old version's lifecycle matters. Documentation and versioning discipline has to be an explicit, standing instruction, not something inferred from the task at hand.

Vulnerable Pattern

// v1 stays live, undocumented, and unpatched after v2 ships — nobody removes it
app.use('/api/v1/users', v1UserRouter); // still running the old auth logic, forgotten
app.use('/api/v2/users', v2UserRouter); // current version

// A quick internal endpoint, never documented, never reviewed
app.get('/internal/debug-user-lookup', async (req, res) => {
  const user = await User.findOne({ email: req.query.email });
  res.json(user); // reachable from the internet, no auth, not in any spec
});

Secure Pattern

// Explicit version registry with lifecycle metadata — not just "it exists"
const API_VERSIONS = {
  v1: { router: v1UserRouter, status: 'deprecated', sunsetDate: '2026-03-01' },
  v2: { router: v2UserRouter, status: 'current' },
};

Object.entries(API_VERSIONS).forEach(([version, { router, status, sunsetDate }]) => {
  if (status === 'deprecated') {
    router.use((req, res, next) => {
      res.set('Deprecation', 'true');
      res.set('Sunset', sunsetDate); // signals deprecation to clients per RFC 8594
      next();
    });
  }
  app.use(`/api/${version}/users`, router);
});

// Every endpoint — including internal ones — is authenticated, authorized,
// and present in the generated OpenAPI spec; nothing exists outside the inventory
app.get('/internal/debug-user-lookup', authenticate, requireAdmin, async (req, res) => {
  const user = await User.findOne({ email: req.query.email }).select('id email createdAt');
  res.json(user);
});
# openapi.yaml — generated in CI, diffed against deployed routes on every build
paths:
  /api/v1/users:
    get: { deprecated: true, ... }
  /api/v2/users:
    get: { ... }
  /internal/debug-user-lookup:
    get: { security: [{ bearerAuth: [] }], ... } # documented, not hidden

Copy-Paste Rules for Cursor, Claude Code, and Codex

---
description: Inventory management — API versioning, deprecation, and documentation discipline
alwaysApply: true
---

# API Inventory Management Rules

- Every new API endpoint MUST be added to the project's OpenAPI/API
  specification in the same change that introduces it — no endpoint should
  exist that isn't documented in the spec.
- When creating a new version of an existing endpoint, explicitly flag the
  previous version's lifecycle status (current, deprecated, sunset date) —
  do not leave an old version running indefinitely without a documented
  deprecation plan.
- Deprecated endpoints must emit deprecation signals to clients (e.g.,
  `Deprecation` and `Sunset` headers per RFC 8594) rather than continuing to
  serve silently with no indication they will be removed.
- There is no such thing as an "internal-only" or "debug" endpoint that
  skips authentication, authorization, or documentation — every endpoint
  reachable over any network gets the full treatment: auth, authz, spec entry.
- Never leave test, example, or scaffolding endpoints generated by a
  framework's starter template in a project intended for production —
  remove them explicitly before considering a feature complete.
- When working across multiple environments (staging, production), do not
  assume configuration or exposure differs safely between them — apply the
  same authentication and inventory documentation standard to every
  environment a build can be deployed to.
- If asked to add a new API version, proactively raise whether the previous
  version should be deprecated as part of the same change, rather than
  treating versions as purely additive.

Verification Checklist

  • Every deployed endpoint has a corresponding entry in the API specification (OpenAPI or equivalent)
  • A CI step generates the spec and diffs it against actually deployed routes, flagging any mismatch
  • Deprecated API versions have documented sunset dates and emit deprecation signals
  • No "internal" or "debug" endpoint skips authentication or documentation
  • Staging and non-production environments are inventoried and monitored to the same standard as production
  • Third-party and webhook-receiving endpoints are included in the same central inventory
  • Continuous discovery validates the inventory against what's actually reachable from the internet — see Trusteed's asset discovery, which finds forgotten and undocumented endpoints automatically

Frequently asked questions

Can API documentation alone solve this problem?

Documentation is necessary but not sufficient — it needs to be continuously validated against what's actually deployed and reachable, since documentation drifts out of date the moment someone deploys a change without updating it. Automated discovery and spec-diffing close that gap.

Why are old, deprecated API versions a security risk if they still function correctly?

Deprecated versions typically stop receiving security patches and updated authorization logic once attention moves to the current version, while remaining fully reachable — an attacker who finds the old version gets access to a codebase with known, unfixed weaknesses that the current version has already addressed.

Should staging and development environments be included in the same inventory as production?

Yes. Staging and development environments frequently contain the same data models and, if misconfigured, similar data — and they're often less rigorously monitored than production, making them an attractive target precisely because they're excluded from standard inventory practices.