getsentry/toolkit

Agent

Agentic tooling for Sentry

getsentryTypeScript, JavaScript, PowerShell, Shell961 starsScanned Nov. 8th, 2026at 6c072bfView source

Trust score

27/100Failing
Likely 1–53 · low confidence

Scanned Nov. 8th, 2026 at 6c072bf against 36 rules · How scoring works

For developersOpen rawGET /c/getsentry/toolkit/report.json
At a glance

At a glance

Contains
21 skills · 2 MCP servers · 84 tools
Its tools can
Delete or overwrite data 12Use secrets and credentials 3Manage identities and access 3Change settings 9Reach any network address 2Returns untrusted content 29Spend money or metered resources 5Post or message on your behalf 1Create or change data 21Read data 58
Issues flagged
1 critical4 high3 medium1 low9 of 207 rule checks, severity as assessed for this repo
Where issues are
  • Repository-wide6 of 17
  • mcp-server/apps/docs-mcp (MCP server)3 of 11
No issues in the other 22 of 24.
Trust signals
Verified publisherUnsigned build52 known CVEs in dependenciesActively maintained

What to fix first

9 issues, most severe first. Each one points to the exact code, with the recommended fix and a prompt you can paste into your coding agent.

  1. 1CriticalNo Authentication or Authorization Checks (General)
    mcp-server/apps/docs-mcp (MCP server) · ATR-M0010
    Both /mcp and /internal/docs are gated only by isDocsMcpServiceRequest, which checks the URL hostname (docs-mcp.internal or localhost) and verifies no caller credential. In production, workers_dev and preview URLs are disabled and no public route is configured, so service-binding isolation holds. However, the shipped local development listener accepts unauthenticated requests addressed to localhost, including requests carrying a hostile Origin. The tools make outbound docs requests and use the operator's shared OpenRouter credential, so the loopback-only exception does not apply and local callers can reach model-backed tool dispatch. This is limited to local development exposure, not a demonstrated public production bypass.
      "scripts": {
        "dev": "wrangler dev --port 8788",
        "build": "wrangler deploy --dry-run",
    Show 2 more locations
    export function isDocsMcpServiceRequest(request: Request): boolean {
      const { url } = request;
      const { hostname } = new URL(url);
      return hostname === "docs-mcp.internal" || LOCAL_HOSTNAMES.has(hostname);
    }
    app.all("/mcp", async (c) => {
      if (!isDocsMcpServiceRequest(c.req.raw)) {
        return c.json(
          { error: "Docs MCP is only available through its service binding" },
          403,
        );
      }

    +3 more locations in the finding JSON

    Recommended fix

    Require a verified credential (for example a shared secret header or signed service-to-service token) in the access check applied to /mcp and /internal/docs, including on the localhost development path, instead of relying on the URL hostname alone.

  2. 2HighDependency Risk & Supply ChainConfirmed exploitable
    Repository-wide · ATR-C0007
    A lockfile exists and installs are frozen, but 24 GitHub Actions steps use mutable tags (semgrep github-actions-mutable-action-tag). These include third-party dawidd6/action-download-artifact@v6 and the checkout, setup-node and github-script actions in eval.yml, which runs on main pushes and dispatch with checks:write. A moved upstream tag would change executable CI code with repository authority, and lockfile integrity hashes do not pin those actions. pnpm-workspace.yaml lists minimumReleaseAgeExclude entries but sets no minimumReleaseAge, trustPolicy or blockExoticSubdependencies, so no release-age or trust policy is in effect. Other workflows pin SHAs, so pinning is inconsistent. Trivy also matches older pinned dependencies against known advisories.
    name: Eval
    
    on:
      workflow_dispatch:
      push:
        branches: [main]
        paths:
          - "packages/mcp-core/src/tools/**"
          - "packages/mcp-server-evals/**"
          - "packages/mcp-server-mocks/**"
          - ".github/workflows/eval.yml"
      pull_request:
    Show 2 more locations
    - uses: actions/checkout@v4
    
          - name: Setup Node.js
            uses: actions/setup-node@v4
    - name: Download main branch token stats
            if: github.event_name == 'pull_request'
            uses: dawidd6/action-download-artifact@v6

    +37 more locations in the finding JSON

    Recommended fix

    Pin every action to a full commit SHA, as deploy.yml and pr-risk-jev.yml already do.

  3. 3HighTool Output Sanitization & Injection ContainmentDeclared behavior
    Repository-wide · ATR-C0013
    MCP tools return third-party-authored Sentry data (snapshot metadata, issue and event text) interpolated into markdown with no untrusted envelope or control/role-marker stripping. A grep found no wrapper or sanitizer in mcp-core.
    function formatImageMetadata(img: SnapshotImageInfo): string[] {
      const lines: string[] = [];
      if (img.display_name) lines.push(`- **Display Name**: ${img.display_name}`);
      if (img.group) lines.push(`- **Group**: ${img.group}`);
      if (img.image_file_name) lines.push(`- **File**: \`${img.image_file_name}\``);
      if (img.description) lines.push(`- **Description**: ${img.description}`);
    Recommended fix

    Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.

  4. 4HighCORS / Origin / Host Validation (DNS Rebinding Defense)
    mcp-server/apps/docs-mcp (MCP server) · ATR-M0003
    The Worker applies hono cors() with defaults and never inspects the Origin header before transport dispatch. isDocsMcpServiceRequest checks only the URL hostname, so a hostile web page can direct requests to the documented localhost development server (for example localhost:8788/mcp or 127.0.0.1:8788/internal/docs) and pass the gate. The tools perform outbound requests and use a shared model credential, so they are not pure-input tools covered by a loopback exception. Private production routing (workers_dev=false, no routes) reduces exposure but does not protect the shipped local HTTP path.
      "scripts": {
        "dev": "wrangler dev --port 8788",
        "build": "wrangler deploy --dry-run",
    Show 2 more locations
    export const app = new Hono<{ Bindings: Env }>().use(cors());
    
    app.get("/health", (c) => c.json({ status: "ok" }));
    
    app.all("/mcp", async (c) => {
      if (!isDocsMcpServiceRequest(c.req.raw)) {
        return c.json(
          { error: "Docs MCP is only available through its service binding" },
          403,
        );
      }
    export function isDocsMcpServiceRequest(request: Request): boolean {
      const { url } = request;
      const { hostname } = new URL(url);
      return hostname === "docs-mcp.internal" || LOCAL_HOSTNAMES.has(hostname);
    }

    +3 more locations in the finding JSON

    Recommended fix

    Add explicit Origin validation (an allowlist, rejecting unexpected Origins) to the /mcp and /internal/docs handlers, and replace the default cors() configuration with one restricted to trusted origins.

  5. 5HighOAuth 2.1 + PKCE Correctness (MCP Authorization)Declared behavior
    mcp-server/apps/docs-mcp (MCP server) · ATR-M0005
    The component exposes a remote streamable-HTTP MCP endpoint (/mcp via @hono/mcp StreamableHTTPTransport) but implements no OAuth 2.1 authorization: no protected-resource metadata, no PKCE, no token validation. Access is restricted only by a URL-hostname check meant for service-binding callers, which is not a verified credential and lets any accepted-hostname caller invoke paid agent operations with the Worker's key. This is a restricted architectural authorization gap rather than demonstrated Internet exploitation: workers_dev and preview URLs are disabled, no public route is configured, and the hostname gate returns 403 for other hostnames.
    app.all("/mcp", async (c) => {
      if (!isDocsMcpServiceRequest(c.req.raw)) {
        return c.json({ error: "Docs MCP is only available through its service binding" }, 403);
      }
      ...
      const transport = new StreamableHTTPTransport();
      const mcp = createMcpServer(c.env.OPENROUTER_API_KEY, c.env.CF_VERSION_METADATA.id);
      await mcp.connect(transport);
      return await transport.handleRequest(c);
    });
    Show 2 more locations
    export function isDocsMcpServiceRequest(request: Request): boolean {
      const { url } = request;
      const { hostname } = new URL(url);
      return hostname === "docs-mcp.internal" || LOCAL_HOSTNAMES.has(hostname);
    }
    try {
            if (!apiKey) {
              throw new Error("Set OPENROUTER_API_KEY on the docs-mcp worker");
            }
    
            const result = await config.run(typedInput, apiKey);
    Recommended fix

    If the endpoint is ever exposed beyond the service binding, front it with OAuth 2.1 (resource metadata, PKCE) or a signed service-to-service token.

Show the other 4 issues
  1. 6MediumLogging Hygiene (No Secrets/PII/Tokens) & Audit Trail
    Repository-wide · ATR-C0005
    The core telemetry scrubber redacts Bearer and Sentry tokens (telem/sentry.ts), but it is not installed in the docs-mcp Worker. docs-mcp MCP instrumentation records complete inputs and outputs, including free-form documentation questions and project stack summaries, and its Sentry initialization enables default PII with no scrubbing hook. Sensitive context or tokens supplied in those strings can be persisted in telemetry. Intentional auth-token stdout commands are not the basis of this finding.
      const mcp = Sentry.wrapMcpServerWithSentry(
        new McpServer(
          {
            description:
              "Generates tailored Sentry integration docs by reading docs.sentry.io.",
            name: "sentry-docs",
            version: "1.0.0",
          },
          { maxToolInputElements: MAX_TOOL_INPUT_ELEMENTS },
        ),
        {
          recordInputs: true,
          recordOutputs: true,
        },
    …
    Show 2 more locations
          stackSummary: z
            .string()
            .optional()
            .describe(
              "Natural-language description of the project's tech stack for this context. Used to discover relevant integration docs beyond the base platform guide.",
            ),
        },
        name: "get-docs",
    export default Sentry.withSentry(
      (_env: Env) => ({
        dsn: "https://05c618aef6e0489b8c9d07e0d65664ce@o1.ingest.us.sentry.io/4511189954199552",
        sendDefaultPii: true,
        tracesSampleRate: 1.0,
      }),
      app,
    );
    Recommended fix

    In apps/docs-mcp, disable recording of full MCP inputs and outputs, or apply the redaction scrubber (for example a beforeSend hook) to telemetry before it is transmitted, and review the default-PII setting in apps/docs-mcp/src/index.ts.

  2. 7MediumMissing or Inadequate Input Validation
    Repository-wide · ATR-C0008
    MCP tools declare typed zod input schemas, but the shared ParamSearchQuery and ParamCursor validators are type-only (query adds trim) with no length or pattern bound. find_projects exposes both directly and forwards them into listProjects API calls. Slug validators are stronger, but they do not cover these fields. A transport-wide body ceiling exists but is not a semantic limit on query or cursor values.
    export const ParamSearchQuery = z
      .string()
      .trim()
      .describe(
        "Search query to filter results by name or slug. Use this to narrow down results when there are many items.",
      );
    
    export const ParamCursor = z
      .string()
      .describe(
        "Pagination cursor from a previous call's nextCursor. Reuse it with the same filters and scope to fetch the next page.",
      );
    
    Show 2 more locations
      inputSchema: {
        organizationSlug: ParamOrganizationSlug,
        regionUrl: ParamRegionUrl.nullable().default(null),
        query: ParamSearchQuery.nullable().default(null),
        cursor: ParamCursor.nullable().default(null),
      },
        const { projects, nextCursor } = await apiService.listProjects(
          organizationSlug,
          {
            query: params.query ?? undefined,
            limit: RESULT_LIMIT,
            cursor: params.cursor ?? undefined,
          },
        );
    Recommended fix

    In packages/mcp-core/src/schema.ts, add explicit maximum lengths (and a character pattern for cursors) to ParamSearchQuery and ParamCursor so find_projects rejects oversized or malformed values before calling listProjects.

  3. 8MediumLack of Output Encoding/Sanitization
    Repository-wide · ATR-C0010
    The server-rendered OAuth pages escape values through an escapeHtml helper, but that does not cover the CLI terminal surface. Sentry issue titles pass through markdown text escaping, which does not remove ESC or other control bytes. Interactive markdown output strips ANSI only in plain mode and writes the styled result directly to stdout. Crafted upstream issue text can therefore inject terminal control sequences that clear or rewrite the operator's display. This is terminal injection, not MCP JSON or OAuth XSS.
      const prefetched = await prefetchIssueDetails(issueArg, spans);
      const { org: orgSlug, issue } = await resolveIssue({
        issueArg,
        cwd,
        command: "view",
      });
    Show 2 more locations
    export function formatIssueDetails(issue: SentryIssue): string {
      const lines: string[] = [];
    
      lines.push(`## ${issue.shortId}: ${text(issue.title ?? "")}`);
      lines.push("");
    /** Puts a value on one line and backslash-escapes `syntax` and line-start syntax. */
    function escapeSyntax(value: string, syntax: RegExp): string {
      return toSingleLine(value)
        .trim()
        .replace(syntax, (char) => `\\${char}`)
        .replace(
          LINE_START_SYNTAX,
          (match) => `${match.slice(0, -1)}\\${match.slice(-1)}`,
        );
    }

    +2 more locations in the finding JSON

    Recommended fix

    Strip ESC and other disallowed terminal control bytes from untrusted values (issue titles and similar upstream text) before applying trusted ANSI styling in the CLI formatters, including the interactive rendering path.

  4. 9LowDoS Protections: Timeouts, Concurrency, Streaming Limits
    Repository-wide · ATR-C0004
    Rate limiting and inbound request caps do not bound outbound snapshot image downloads. get_snapshot_image calls fetchImageByUrl, which buffers the full response body into a blob and converts it to base64 with no response-size ceiling and no body-read timeout. fetchWithTimeout clears its abort timer once headers return, not after the body is consumed. A caller able to inspect snapshots can request full-resolution assets and force large buffering. This resource-consumption gap does not depend on proving who controls the image URL's host.
          .enum(["preview", "full"])
          .describe(
            "Return locally generated previews or original full-resolution bytes. Full-resolution images can substantially increase response size and token usage.",
          )
          .default("preview"),
        regionUrl: ParamRegionUrl.nullable().default(null),
      },
    Show 2 more locations
          params.snapshotId,
          params.imageIdentifier,
          params.imageResolution,
          {
            experimentalMode: context.experimentalMode ?? false,
            availableToolNames: context.availableToolNames,
            directToolNames: context.directToolNames,
      async fetchImageByUrl(
        imageUrl: string,
      ): Promise<{ blob: Blob; contentType: string }> {
        const response = imageUrl.startsWith("https://")
          ? await fetch(imageUrl)
          : await this.request(
              imageUrl.startsWith("/api/0")
                ? imageUrl.slice("/api/0".length)
                : imageUrl,
            );
        if (!response.ok) {
          throw new Error(
            `Failed to fetch image: ${response.status} ${response.statusText}`,
          );
    …

    +1 more location in the finding JSON

    Recommended fix

    In fetchImageByUrl (packages/mcp-core/src/api-client/client.ts), enforce a maximum response size by checking Content-Length and streaming the body with a byte cap. Keep the abort timer active until the body is fully read, and only then convert to base64.

What’s inside

BlueRock found 23 components in this repository. Each is checked against the rules for its type.

MCP servermcp-server/apps/docs-mcpapps/docs-mcp47Weak1 critical2 high

Its tools can

Returns untrusted content 4Spend money or metered resources 3Read data 4

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

MCP server rules 3 of 11 flagged

6 rules passed · 2 did not applyShow
PassedTool Definition Integrity & Versioning (ETDI-style)ATR-M0001Details

Why it got this result

Tool definitions are compiled into the Worker (apps/docs-mcp/src/mcp.ts registerTool calls) and deployed via wrangler; no remote tool manifest, hot reload or runtime-fetched definitions exist. Tools are fixed per deployed version.

Why it matters

Prevents rug-pull tool poisoning.

Evidence

mcp.registerTool(
    "search-docs",
    {
      description:
        "Search the public Sentry docs index. Returns deterministic ranked page titles...",
      inputSchema: searchDocsInputSchema.shape,
PassedUnrestricted Tool Permissions (Overprivileged Tools)ATR-M0002Details

Why it got this result

All four tools (get-available-features, search-docs, query-docs, get-docs) are read-only and only reach docs.sentry.io (fetcher BASE_URL fixed) plus the OpenRouter LLM API. No fs, exec or admin actions are exposed. Caller-controlled inputs are zod-validated.

Why it matters

Limits blast radius and privilege escalation.

Evidence

const BASE_URL = "https://docs.sentry.io";
PassedCORS Misuse: Wildcard with CredentialsATR-M0008Details

Why it got this result

hono cors() is used with defaults (Access-Control-Allow-Origin: * and credentials not enabled). No cookies or credentialed responses are used, so wildcard+credentials does not occur.

Why it matters

Prevents credentialed cross-origin abuse.

Evidence

export const app = new Hono<{ Bindings: Env }>().use(cors());
PassedTransport Security: TLS Everywhere (Remote)ATR-M0009Details

Why it got this result

Runs on Cloudflare Workers where TLS is terminated by the platform; outbound fetches target https://docs.sentry.io. No plain-HTTP server or disabled certificate validation in code.

Why it matters

MitM can steal tokens and tamper responses.

Evidence

const BASE_URL = "https://docs.sentry.io";
PassedTool Name Collision / SpoofingATR-M0006Details

Why it got this result

Tool names (get-available-features, search-docs, query-docs, get-docs) are generic verbs scoped to Sentry docs; this server is consumed only through an internal service binding, not alongside third-party servers. No specific named collision with a widely-deployed server tool was established.

Why it matters

Prevents malicious tool shadowing.

Evidence

name: "get-available-features",
Show 1 more location
PassedPrompt/Metadata Injection in Tool DescriptionsATR-M0007Details

Why it got this result

All four registered tool descriptions in mcp.ts are neutral functional descriptions with no directives, hidden characters or exfiltration instructions.

Why it matters

Prevents covert model manipulation.

Evidence

description:
        "Answer a free-form Sentry documentation question using fetched docs.sentry.io pages. The result contains a cited Markdown answer and its validated source URLs.",
Does not applyOAuth Proxy Per-Client Consent (Confused Deputy Defense)ATR-M0011Details

Why it got this result

The server is not an OAuth proxy (no /authorize, no client registration, no upstream IdP). Tools take no caller-supplied target for the shared OpenRouter credential; they only run LLM prompts over docs.sentry.io content.

Why it matters

Shared upstream client + dynamic MCP-client registration + no per-client consent enables attacker-registered MCP clients to receive bridge codes carrying victim upstream tokens — the documented MCP Confused Deputy attack.

Does not applyBinding to 0.0.0.0 Without Auth/TLSATR-M0004Details

Why it got this result

Cloudflare Worker; no socket listen/bind in code. Exposure is controlled by workers_dev=false and the service-binding hostname gate; there is no 0.0.0.0 bind and no Dockerfile/Ingress.

Why it matters

LAN/Internet exposure enables remote exploitation.

Show its 4 tools
ToolIt can
get-available-features
Returns canonical Sentry feature slugs for given libraries via an LLM agent reading docs.sentry.io
Read dataReturns untrusted contentSpend money or metered resources
get-docs
Returns a tailored Sentry integration guide synthesized by an LLM agent from live docs
Read dataReturns untrusted contentSpend money or metered resources
query-docs
Answers free-form Sentry docs questions using fetched docs.sentry.io pages with cited sources
Read dataReturns untrusted contentSpend money or metered resources
search-docs
Deterministic search of the public Sentry docs index
Read dataReturns untrusted content

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.agents/skills/create-pr.agents/skills/create-pr100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

SKILL.md is a pure alias pointing to sentry-skills:pr-writer. No bundled scripts, no network/exec/credential access. The component directory holds only this file.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

Use `sentry-skills:pr-writer` as the canonical skill for creating and editing pull requests.

If invoked via `create-pr`, run the same workflow and conventions documented in `sentry-skills:pr-writer`.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

Alias text only, with no directives about confirmation, disclosure or authority.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

This skill name is kept for compatibility.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The frontmatter declares only name and description. There is no allowed-tools, wildcard, OAuth scope, bin or install hook.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

name: create-pr
description: Alias for sentry-skills:pr-writer. Use when users explicitly ask for "create-pr" or reference the legacy skill name. Redirects to the canonical PR writing workflow.
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A codepoint scan of the model-facing text found no zero-width, bidi or Tag-block characters. There are no HTML comments, forged role tokens or ANSI escapes, and the text is plain ASCII.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

Use `sentry-skills:pr-writer` as the canonical skill for creating and editing pull requests.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Instructions ship inside the repo. There is no runtime fetch of the artifact's own instructions, no self-rewriting and no launch entry.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

# Alias: create-pr

This skill name is kept for compatibility.
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name 'create-pr' is a common-word name with no near-miss of a brand. It is published from the getsentry GitHub org and aliases a first-party Sentry skill.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

Alias skill that reads no credential store or conversation context and has no outbound channel.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The directory ships only SKILL.md. There are no scripts, hooks, host config or env assignments.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.agents/skills/logging-observability.agents/skills/logging-observability100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

Pure-prose SKILL.md; ships only references/guidance for reviewing logging code. No bundled scripts, network clients, exec or credential reads.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

name: logging-observability
description: Review code for correct logging and error handling patterns. Use when reviewing code that handles errors...
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

Grepped the SKILL.md for consent-suppression/disclosure-hiding phrases (never ask, auto-approve, skip confirmation, don't tell, pre-authorized): none found. YARA hits in the repo were in other files (packages/cli/AGENTS.md etc.), not this component.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

# Logging & Observability Correctness

Enforce correct logging and error handling patterns in the sentry-mcp codebase.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

Frontmatter declares only name/description; no allowed-tools, scopes, wildcards, bins or install hooks.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

---
name: logging-observability
description: Review code for correct logging and error handling patterns...
---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

Codepoint scan found no zero-width, bidi, Tag-block or ANSI characters; no HTML comments, forged chat-template tokens or host-directed injection imperatives.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

description: Review code for correct logging and error handling patterns. Use when reviewing code that handles errors...
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Instructions ship inside the bundle; no runtime fetch, self-updater, self-rewrite or launch entry.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

---
name: logging-observability
description: Review code for correct logging...
---
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

Generic descriptive name; not a near-miss of a known brand; published in getsentry/toolkit repo.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

Skill reads no host credential store or conversation context and has no outbound channel; no harvest+exfil pair is constructible.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

Directory contains only SKILL.md; no bundled scripts, hooks, config or env assignments.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.agents/skills/mcp-audit.agents/skills/mcp-audit100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

Pure-declarative skill: SKILL.md plus markdown references. No bundled scripts, network clients, exec or credential reads. The declared purpose (MCP spec audit) matches the content.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

Grep of the skill for auto-approve, skip-confirmation and silent-action prose found nothing. The instructions are audit workflow guidance only.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

# MCP Audit

Audit an MCP server against the current released MCP specification and any repo-specific compatibility constraints.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The frontmatter declares only name and description, with no allowed-tools, wildcard or OAuth scopes, and the skill ships no executables.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A codepoint scan for zero-width, bidi and Tag-block characters over the skill files returned no matches. There are no forged role tokens or host-directed override imperatives.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

Read `references/spec-baseline.md` and `references/checklist.md` before making changes.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

The instructions and references ship inside the bundle. There is no runtime fetch of the skill's own instructions, no self-updater and no launch entry.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

Read `references/spec-baseline.md` and `references/checklist.md` before making changes.
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name 'mcp-audit' is not a near-miss of a specific brand, and the artifact claims no official or fork status. The source org is getsentry.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

The skill reads no host credential store or conversation context and ships no outbound channel; there is no harvest+exfil pair.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The directory contains only markdown files. It ships no hooks, scripts, dotenv, env assignments or settings.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.agents/skills/mcp-qa.agents/skills/mcp-qa100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

7 rules passed · 1 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

SKILL.md instructs running pnpm build/test/lint, the local sentry CLI, authenticated requests against production Sentry, and an optional curl to localhost:5173. These operations are explicitly declared and match the stated purpose of end-to-end QA for MCP tool changes. There are no bundled scripts, no concealment and no undisclosed third-party egress.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

pnpm run tsc && pnpm run lint && pnpm run test
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The skill tells the agent to avoid live prod mutation unless a disposable resource exists. It has no instructions to skip confirmations, hide actions or override host policy.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

For mutating tools, avoid live prod changes unless there is a disposable
PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

The skill directs the agent to use the local Sentry CLI's stored credentials (sentry auth status/login) and permits an access-token argument for authenticated requests against production Sentry. This delegated credential access is disclosed and functional for end-to-end MCP QA. The skill has no directive to read env or credential files, dump conversation context, or send secrets to an unrelated external collector; the only curl target is localhost.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The frontmatter declares no allowed-tools or scopes. It is a passive prose artifact with no executables or install hooks.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

name: mcp-qa
description: QA MCP tool changes with local CLI and real agent clients. Use when explicitly invoked via /mcp-qa or when asked to QA MCP tool changes end-to-end.
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A scan of the skill directory found no zero-width, bidi or Tag-block characters, no HTML comments and no injection imperatives aimed at the host model.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

Verify MCP tool behavior end-to-end before committing or creating a PR. Prefer
agent-callable paths over browser or inspector workflows.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Instructions are static and bundled. The skill fetches no instructions, rewrites no manifest and has no unpinned npx/uvx launch.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

pnpm run tsc && pnpm run lint && pnpm run test
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name 'mcp-qa' is generic with no brand near-miss, and the skill makes no official or replacement claims.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The directory ships only SKILL.md, which invokes pnpm/CLI commands through the host's normal tool flow. No bundled scripts, hooks, dotenv or exec-enabling env vars.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.agents/skills/openspec-apply-change.agents/skills/openspec-apply-change100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

Prose-only skill that drives the declared openspec CLI to implement tasks; no bundled code, no undeclared network/exec/credential access.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

name: openspec-apply-change
description: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.
license: MIT
compatibility: Requires openspec CLI.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

No consent-suppression prose; skill explicitly defers to the user via the AskUserQuestion tool when ambiguous.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

Frontmatter has no allowed-tools or scopes; only declares the openspec CLI requirement; no executables or install hooks.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

---
name: openspec-apply-change
description: Implement tasks from an OpenSpec change...
license: MIT
compatibility: Requires openspec CLI.
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

Codepoint scan clean (no concealment characters); no HTML comments or forged role tokens.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

description: Implement tasks from an OpenSpec change. Use when the user wants to start implementing...
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Instructions are bundled; no live-fetched manifest, no unpinned launch entry.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

---
name: openspec-apply-change
...
compatibility: Requires openspec CLI.
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

Name matches the upstream OpenSpec generated skill naming (openspec-apply-change); no near-miss, trust prefix or impersonation prose.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

Only invokes the openspec CLI on local change artifacts; no credential/context harvesting or exfil channel.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

Only SKILL.md; ships no scripts, hooks, dotenv or env-hijack primitives.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.agents/skills/openspec-archive-change.agents/skills/openspec-archive-change100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

7 rules passed · 1 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

The skill is prose instructions that call the declared openspec CLI. It bundles no scripts and has no network or credential access beyond its stated purpose.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The skill defers to the host consent flow and asks the user before archiving.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

   - Use **AskUserQuestion tool** to confirm user wants to proceed
   - Proceed if user confirms
PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

The skill reads conversation context only to infer which change to archive, as stated at line 14 of SKILL.md. It uses that value locally for archival and does not export conversation content, collect credentials, or send data to any outbound channel.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The frontmatter declares no allowed-tools, wildcards or OAuth scopes, and the skill ships no executables.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A codepoint scan found no zero-width, bidi or Tag characters. There are no forged role tokens or override directives.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

The instructions ship in the bundle. There is no runtime fetch of the skill's own instructions, no self-update and no launch entry.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

  author: openspec
  version: "1.0"
  generatedBy: "1.4.1"
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name follows the upstream OpenSpec generator's canonical naming. It is not a near-miss of another brand and makes no official or fork claim.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The directory holds only SKILL.md. It ships no hooks, scripts, env assignments or host-config writes.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.agents/skills/openspec-explore.agents/skills/openspec-explore100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

The skill is a prose-only thinking-partner skill with no bundled code, network access or credential access. Its prohibition covers implementing application code, not all writes: SKILL.md line 14 expressly permits user-requested OpenSpec planning artifacts. These disclosed planning writes and CLI calls are consistent with the skill's exploration purpose and involve no concealed or undeclared capability.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The skill restricts the agent (no implementing) and has no consent-bypass or disclosure-suppression prose.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

If the user asks you to implement something, remind them to exit explore mode first and create a change proposal.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The only requirement declared is the openspec CLI. There is no allowed-tools, wildcard or scope, and no bundled executables.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

compatibility: Requires openspec CLI.
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A scan found no concealment codepoints, hidden HTML comments or forged chat tokens. The only directives are task-scoped behavioral guidance.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Instructions are static and bundled. There is no remote fetch, self-modification or launch entry.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

compatibility: Requires openspec CLI.
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name matches the canonical openspec-explore skill generated by the openspec CLI (author: openspec). It is a correct name, not a near-miss, and makes no official or replacement claims.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

Prose-only thinking skill. It reads no credentials, dumps no context, and has no outbound channel.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The directory ships only SKILL.md and no execution or control-plane primitives.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.agents/skills/openspec-propose.agents/skills/openspec-propose100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

Prose-only skill invoking the declared openspec CLI; behavior matches description.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

name: openspec-propose
description: Propose a new change with all artifacts generated in one step...
license: MIT
compatibility: Requires openspec CLI.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

No consent-suppression, disclosure-hiding or authority-override prose found by keyword search and read-through.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

Propose a new change - create the change and generate all artifacts in one step.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

No allowed-tools/wildcards/scopes declared; passive prose artifact.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

---
name: openspec-propose
description: Propose a new change with all artifacts generated in one step...
license: MIT
compatibility: Requires openspec CLI.
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

Codepoint scan clean; no hidden or forged directives.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

description: Propose a new change with all artifacts generated in one step...
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Instructions are bundled; no live fetch, self-mutation or launch entry.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

---
name: openspec-propose
...
compatibility: Requires openspec CLI.
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

Canonical OpenSpec generated skill name; no impersonation signals.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

Only invokes the openspec CLI on local artifacts; no credential/context reads or outbound sink.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

Only SKILL.md; no format-native execution or control-plane primitives.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.agents/skills/openspec-sync-specs.agents/skills/openspec-sync-specs100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

The skill is prose instructions that call the declared openspec CLI. It bundles no scripts and has no undeclared network or credential access.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

compatibility: Requires openspec CLI.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The skill uses AskUserQuestion to let the user select a change. It has no directives to skip confirmation.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The frontmatter declares no allowed-tools, wildcards or scopes, and the skill ships no executables.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A codepoint scan found no concealment characters, forged role tokens or override directives.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

   Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

The instructions ship in the bundle. There is no live fetch, no self-mutation and no launch entry.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

  author: openspec
  version: "1.0"
  generatedBy: "1.4.1"
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name follows the canonical OpenSpec naming. It is not a near-miss and makes no impersonation claim.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

The skill has no credential or context reads and no outbound channel.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The directory holds only SKILL.md. It ships no hooks, scripts or env assignments.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.agents/skills/skill-creator.agents/skills/skill-creator100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

SKILL.md is a pure alias pointing to sentry-skills:skill-writer. No bundled code or undisclosed capability.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

Use `sentry-skills:skill-writer` as the canonical skill for creating and updating skills.

If invoked via `skill-creator`, run the same workflow and conventions documented in `sentry-skills:skill-writer`.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

Alias text only, with no consent-related directives.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

This skill name is kept for compatibility.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The frontmatter declares only name and description. There is no allowed-tools, wildcard, OAuth scope, bin or install hook.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

name: skill-creator
description: Alias for sentry-skills:skill-writer. Use when users explicitly ask for "skill-creator" or reference the legacy skill name. Redirects to the canonical skill authoring workflow.
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A codepoint scan of the model-facing text found no concealment characters, no HTML comments and no forged tokens.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

Use `sentry-skills:skill-writer` as the canonical skill for creating and updating skills.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Instructions ship inside the repo. There is no runtime fetch, self-mutation or launch entry.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

# Alias: skill-creator

This skill name is kept for compatibility.
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name 'skill-creator' is a generic name with no brand near-miss. The publisher is the getsentry org (origin https://github.com/getsentry/toolkit).

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

Alias skill that reads no credential store or conversation context and has no outbound channel.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The directory ships only SKILL.md. There are no scripts, hooks, host config or env assignments.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.agents/skills/testing-guidelines.agents/skills/testing-guidelines100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

Pure guidance document on writing tests; no capabilities beyond text.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

name: testing-guidelines
description: Guide for writing tests. Use when adding new functionality, fixing bugs, or when tests are needed.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

Testing guidance only; no directives about confirmation, disclosure or authority.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

name: testing-guidelines
description: Guide for writing tests.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

Frontmatter is name/description only; no grants, no bundled executables.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

---
name: testing-guidelines
description: Guide for writing tests. Use when adding new functionality...
---
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

Codepoint scan clean; neutral descriptive text.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

description: Guide for writing tests. Use when adding new functionality, fixing bugs, or when tests are needed.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Static bundled guidance; no runtime fetch or mutation.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

---
name: testing-guidelines
description: Guide for writing tests...
---
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

Generic name, no brand near-miss.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

Pure guidance text; no reads of credentials or context, no egress.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

Only SKILL.md; no format-native execution or control-plane primitives.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.codex/skills/openspec-apply-change.codex/skills/openspec-apply-change100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

The skill is prose instructions that call the declared openspec CLI. It bundles no scripts and has no undeclared capabilities.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

compatibility: Requires openspec CLI.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The skill asks the user to select when the change is ambiguous. It has no directives to skip confirmation.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The frontmatter declares no allowed-tools, wildcards or scopes, and the skill ships no executables.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A codepoint scan found no concealment characters, forged role tokens or override directives.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

   - If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

The instructions ship in the bundle. There is no live fetch, no self-mutation and no launch entry.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

  author: openspec
  version: "1.0"
  generatedBy: "1.4.1"
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name follows the canonical OpenSpec naming. It is not a near-miss and makes no impersonation claim.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

The skill has no credential or context reads and no outbound channel.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The directory holds only SKILL.md. It ships no hooks, scripts or env assignments.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.codex/skills/openspec-archive-change.codex/skills/openspec-archive-change100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

Runs only the openspec CLI, mkdir and mv within the change directory, which matches the declared archive purpose. No egress or credential reads.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

   mv "<changeRoot>" "<planningHome.changesDir>/archive/YYYY-MM-DD-<name>"
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The skill requires AskUserQuestion for change selection and for incomplete artifacts/tasks, and says not to guess or auto-select a change. It defers to the consent gate.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The only requirement declared is the openspec CLI. There is no allowed-tools, wildcard or scope, and no bundled executables.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A scan found no concealment codepoints, hidden comments or forged tokens.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Instructions are static and bundled. It does not edit its own SKILL.md (it only moves openspec change directories) and fetches nothing.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

   mv "<changeRoot>" "<planningHome.changesDir>/archive/YYYY-MM-DD-<name>"
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name is the canonical openspec-archive-change skill name from the openspec generator and is not a near-miss.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

Only local openspec and filesystem moves. No credential or context reads and no outbound channel.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The directory ships only SKILL.md. It has no hooks, scripts or covert host-state writes. Archiving is its declared, user-confirmed function.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.codex/skills/openspec-explore.codex/skills/openspec-explore100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

The skill is text-only, but it instructs use of the openspec CLI and permits user-requested planning artifact writes (line 14). These effects fit the declared thinking-partner purpose, and no bundled executable or hidden capability was found.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

name: openspec-explore
description: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

Exploration-mode prose with no consent-suppression or oversight-disabling directives (keyword search clean).

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

name: openspec-explore
description: Enter explore mode - a thinking partner for exploring ideas...
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

No allowed-tools, scopes or wildcard grants; passive prose artifact.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

---
name: openspec-explore
description: Enter explore mode - a thinking partner for exploring ideas...
license: MIT
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

Codepoint scan clean; no hidden or forged directives.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

description: Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Static bundled prose; no runtime fetch or mutation.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

---
name: openspec-explore
description: Enter explore mode...
license: MIT
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

Canonical OpenSpec generated skill name; no impersonation signals.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

Prose-only exploration skill; no credential/context harvesting or egress.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

Only SKILL.md; no format-native execution or control-plane primitives.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.codex/skills/openspec-propose.codex/skills/openspec-propose100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

The skill is prose instructions that call the declared openspec CLI. It bundles no scripts and has no undeclared capabilities.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

compatibility: Requires openspec CLI.
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The skill asks the user via AskUserQuestion. It has no directives to skip confirmation.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The frontmatter declares no allowed-tools, wildcards or scopes, and the skill ships no executables.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A codepoint scan found no concealment characters, forged role tokens or override directives.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

   Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

The instructions ship in the bundle. There is no live fetch, no self-mutation and no launch entry.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

  author: openspec
  version: "1.0"
  generatedBy: "1.4.1"
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name follows the canonical OpenSpec naming. It is not a near-miss and makes no impersonation claim.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

The skill has no credential or context reads and no outbound channel.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The directory holds only SKILL.md. It ships no hooks, scripts or env assignments.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/.codex/skills/openspec-sync-specs.codex/skills/openspec-sync-specs100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

Runs openspec status and edits openspec/specs files, which matches the declared purpose. No egress, credential reads or concealment.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

   openspec status --change "<name>" --json
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The skill requires prompting for change selection and has no suppression prose.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The only requirement declared is the openspec CLI. There is no allowed-tools, wildcard or scope, and no bundled executables.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

compatibility: Requires openspec CLI.
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A scan found no concealment codepoints, hidden comments or forged tokens.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

   **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Instructions are static and bundled. It edits only openspec/specs data files, not its own manifest, and fetches nothing.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

   openspec status --change "<name>" --json
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name is the canonical openspec-sync-specs skill name from the openspec generator and is not a near-miss.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

Only local openspec and spec file edits. No credential or context reads and no outbound channel.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The directory ships only SKILL.md. It has no hooks or scripts, and spec edits are its declared function.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/packages/cli/.cursor/skills/sentry-clipackages/cli/.cursor/skills/sentry-cli100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

7 rules passed · 1 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

SKILL.md (symlink to packages/cli/plugins/sentry-cli/skills/sentry-cli/SKILL.md) is a documentation skill for the declared sentry CLI. No bundled scripts; commands shown match the stated purpose. The YARA credential-exfil hit (L159-255) covers ordinary CLI examples (sentry docs, status, schema, release, api); attestation unavailable on this page.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

name: sentry-cli
version: 0.49.0-dev.0
description: Guide for using the Sentry CLI to interact with Sentry from the command line...
requires:
  bins: ["sentry"]
  auth: true
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

Guidance such as 'just run the command' concerns org/project auto-detection, not a bypass of host consent; the CLI prompts for login. No disclosure suppression or authority-override prose.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

> **Core rule for agents: just run the command.** The `sentry` CLI auto-detects your org and project...
PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

The skill documents delegated credential handling: references/auth.md (lines 211-220) shows the sanctioned CLI printing the stored auth token, so credentials are read by delegation. No directive couples that read with a send to a third-party sink, and DSN detection from .env is performed by the declared CLI for its own function. Declared authentication is handled safely with no harvest-and-exfiltrate pairing.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

Manifest declares only the sentry binary and auth requirement, matching its purpose; no wildcard allowed-tools, scopes or install hooks.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

Codepoint scan found no zero-width, bidi, Tag-block or ANSI characters; no HTML comments or forged chat-template tokens.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

description: Guide for using the Sentry CLI to interact with Sentry from the command line. Use when the user asks about viewing issues...
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

Instructions ship inside the bundle with a version in frontmatter; no live fetch of instructions, self-rewrite or launch entry. The documented curl installer for the CLI binary is user-run documentation, not an instruction fetch.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

Published from the getsentry GitHub org (origin https://github.com/getsentry/toolkit), the genuine Sentry owner; name sentry-cli is canonical.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The component directory is symlinks to a SKILL.md and references folder; no bundled hooks, scripts, dotenv, git hooks or exec-enabling env assignments. (The sibling packages/cli/.cursor/hooks.json is a repo dev lint hook, not part of the skill.)

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/packages/cli/plugins/sentry-clipackages/cli/plugins/sentry-cli100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

7 rules passed · 1 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

The skill is documentation for the declared sentry CLI and bundles only markdown references. It has no scripts, no hidden network clients and no credential reads. The commands it documents (issue/event/api/auth) match the stated purpose, and the curl | bash lines are the documented user install step for the CLI, not a runtime egress path.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

---
name: sentry-cli
version: 0.49.0-dev.0
description: Guide for using the Sentry CLI to interact with Sentry from the command line. ...
requires:
  bins: ["sentry"]
  auth: true
---
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The Safety Rules section defers to user consent. The YARA hit on this file (Zeus_Metadata_Credential_Exfiltration_Directive_WideSpan, L159-255; the attestation was not available on this page) lands on CLI usage examples (docs, status, schema, release, api). None directs credential harvesting or consent suppression. Line 52 says the opposite: never store or log tokens.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

- Always confirm with the user before running destructive commands: `alert issues delete`, ... `project delete`, `release delete`, `token delete`, `trial start`
- For mutations, verify the org/project context looks correct in the command output before proceeding with further changes
- Never store or log authentication tokens — the CLI manages credentials automatically
PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

The bundled auth reference (references/auth.md, lines 211-220) directs the CLI to print a stored token, so credential handling is in scope for this rule. It is declared Sentry authentication functionality with in-band output. Nothing directs the agent to send credentials or conversation context to an external destination, and the skill tells the agent never to store or log tokens.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The manifest declares only a required binary (sentry) and auth. There is no allowed-tools wildcard, no OAuth scope block and no postinstall. The declared reach matches the CLI skill's stated function.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

{
  "name": "sentry-cli",
  "version": "0.49.0",
  "description": "Skills for using the Sentry CLI to interact with Sentry from the command line"
}
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A codepoint scan of the skill and plugin files found no zero-width, bidi or Tag characters. There are no forged role tokens. The one 'Core rule for agents' line is ordinary usage guidance scoped to the CLI, not an override of host policy.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

> **Core rule for agents: just run the command.** The `sentry` CLI auto-detects your org and project ...
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

The skill text ships inside the bundle. The CLI's agent-skill installer embeds the generated SKILL.md, so no network fetch of the skill's own instructions is needed. There is no launch entry and no unpinned package fetch. The curl | bash lines are the documented user install step for the CLI binary, not the skill's instructions.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

{
  "name": "sentry-cli",
  "version": "0.49.0",
  "description": "Skills for using the Sentry CLI to interact with Sentry from the command line"
}
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The name 'sentry-cli' is published from the getsentry org repo (origin https://github.com/getsentry/toolkit). The publisher is verifiably the brand owner.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The plugin directory ships only SKILL.md, markdown references and a plugin.json with name/version/description. There are no hooks, entry scripts, env assignments or host-config writes.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/packages/cli/plugins/sentry-cli/skills/sentry-clipackages/cli/plugins/sentry-cli/skills/sentry-cli100StrongNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

7 rules passed · 1 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

SKILL.md and references/*.md document the sentry CLI and ship no scripts. The declared purpose matches the commands documented. The curl https://cli.sentry.dev/install | bash step is a disclosed installation step to the first-party host, and the manifest declares requires bins sentry and auth. The YARA credential-exfiltration match on SKILL.md (L159-255) was reviewed: it covers ordinary CLI usage examples, not a harvest-and-send directive. The YARA scanner_attestation was not available in this session.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

### Installation

```bash
curl https://cli.sentry.dev/install -fsS | bash
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The Safety Rules section says to always confirm with the user before destructive commands. The 'just run the command' guidance concerns org/project discovery and does not skip consent for state-changing actions.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

- Always confirm with the user before running destructive commands: `alert issues delete`, `alert metrics delete`, `cli uninstall`, `dashboard widget delete`, `integration delete`, `issue merge`, `issue unlink`, `project delete`, `release delete`, `token delete`, `trial start`
PassedHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

The skill documents sentry auth token, which prints the stored token to the user. It also documents CLI auth handling. It does not direct the agent to append credentials to any request or send them to a third party, and the Safety Rules say never to store or log tokens. The only egress is the CLI's calls to Sentry itself. The YARA Zeus_Metadata_Credential_Exfiltration_Directive_WideSpan match on SKILL.md (L159-255) was reviewed and is ordinary CLI usage examples with no harvest-plus-send directive. The scanner_attestation was not available in this session.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Evidence

- Never store or log authentication tokens — the CLI manages credentials automatically
Show 1 more location
- `sentry auth token` — Print the stored authentication token
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The manifest requires only the sentry binary and auth, which match the stated function of using the Sentry CLI. There is no allowed-tools wildcard, no OAuth scope block, and no postinstall or hook. plugin.json has only name, version and description.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

Show 1 more location
  "name": "sentry-cli",
  "version": "0.49.0",
  "description": "Skills for using the Sentry CLI to interact with Sentry from the command line"
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A grep for zero-width, bidi and Tag-block codepoints across SKILL.md and references/ returned no matches. The only non-ASCII characters are typographic dashes. Directives such as 'just run the command' are visible, task-scoped usage guidance, not hidden or injected. The YARA instruction-override hit is on packages/cli/AGENTS.md, which is outside this component.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

> **Core rule for agents: just run the command.** The `sentry` CLI auto-detects your org and project (from `.sentryclirc`, DSNs in `.env`/source, and the directory name)
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

SKILL.md and references ship in the bundle with a recorded version. The skill has no .mcp.json or launch entry and does not self-modify. The documented install and upgrade examples (SKILL.md:359-365) are explicit, user-run commands for the first-party CLI installer, not a shipped launch entry or a silent mutation of the skill's own instructions. The SENTRY_VERSION pin selects the CLI release but does not pin or authenticate the installer script bytes. The YARA remote-bootstrap match is on packages/cli/install.ps1, which is outside this component.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

curl https://cli.sentry.dev/install -fsS | SENTRY_VERSION=0.42.2 bash
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The skill is named for the Sentry CLI and is published from the getsentry GitHub org (origin https://github.com/getsentry/toolkit), the brand's genuine owner. Install URLs point to the first-party cli.sentry.dev, with no look-alike registry.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The skill directory and its .claude-plugin/plugin.json contain only markdown and a name/version/description manifest. There are no hooks, entry scripts, dotenv or exec-enabling/endpoint env assignments. The curl installer in the docs is a user-run install step for the CLI, not a bundled load-time primitive.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
MCP servermcp-server/packages/mcp-corepackages/mcp-core67AdequateNone flagged

Its tools can

Delete or overwrite data 12Use secrets and credentials 3Manage identities and access 3Change settings 9Reach any network address 2Returns untrusted content 25Spend money or metered resources 2Post or message on your behalf 1Create or change data 21Read data 54

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

MCP server rules 0 of 11 flagged

4 rules passed · 7 did not applyShow
PassedTool Definition Integrity & Versioning (ETDI-style)ATR-M0001Details

Why it got this result

Tool definitions are compiled into the package (src/tools/catalog/*.ts, generated toolDefinitions.json) and registered in-process via server.registerTool. There is no remote tool manifest and no hot reload over HTTP. The set is fixed per server (listChanged: false). The package is not individually code-signed, but definitions are immutable once built.

Why it matters

Prevents rug-pull tool poisoning.

Evidence

for (const { name, config, handler } of registrations) {
    server.registerTool(name, config, handler);
  }
  // Registration enables this by default, but each server's tool catalog is fixed.
  if (registrations.length > 0) {
    server.server.registerCapabilities({ tools: { listChanged: false } });
PassedUnrestricted Tool Permissions (Overprivileged Tools)ATR-M0002Details

Why it got this result

Tools are Sentry-API wrappers with no fs, exec or arbitrary-network reach. Runtime availability is gated by grantedSkills, mode and project constraints in catalog-runtime/availability.ts; per-tool requiredScopes declarations and readOnly/destructive annotations are present in the catalog but are not directly checked in that availability path. The execute_sentry_tool dispatcher is annotated destructive, but it re-resolves the target tool through the same availability filter and errors on unavailable targets, so it does not widen privilege.

Why it matters

Limits blast radius and privilege escalation.

Evidence

name: "delete_alert_rule",
  requiredScopes: ["org:read", "project:read", "alerts:write"],
  annotations: {
    readOnlyHint: false,
    destructiveHint: true,
    idempotentHint: true,
    openWorldHint: true,
  },
Show 1 more location
      const availableTools = getSearchableTools({
        tools,
        context,
        experimentalMode: context.experimentalMode ?? false,
        useDefaultSurfacePolicy: true,
      });
      const matchedTool = availableTools.find(({ key, tool }) => key === name || tool.name === name)?.tool ...
        availableTools.find(
          ({ key, tool }) => key === name || tool.name === name,
PassedTool Name Collision / SpoofingATR-M0006Details

Why it got this result

Tool names are Sentry-domain identifiers. None shadows a named tool of a widely deployed server (filesystem, git, github). Generic names such as whoami or get_doc have no specific collision. Deprecated aliases are explicitly marked, and serverInfo.name is 'Sentry MCP'.

Why it matters

Prevents malicious tool shadowing.

Evidence

  name: "get_ai_conversation_details",
  title: "Get AI Conversation Details (Deprecated)",
  includeInSkillDefinitions: false,
  deprecated: { replacedBy: ["get_agent_conversation_details"] },
PassedPrompt/Metadata Injection in Tool DescriptionsATR-M0007Details

Why it got this result

I reviewed the registered tool descriptions (e.g. execute_sentry_tool) and grepped src/tools for override directives, concealment phrases, ANSI escapes and bidi/zero-width codepoints. None were found. Descriptions are task-descriptive usage guidance. The YARA hit on tools/support/search-events/config.ts is an internal LLM-agent system prompt for query translation (field-verification guidance), not a registered tool description, and contains no credential-exfiltration directive. The YARA attestation was not available on this page.

Why it matters

Prevents covert model manipulation.

Evidence

description: [
      "Execute an available Sentry MCP tool discovered through search_sentry_tools.",
      "",
      "Use this tool when you need to:",
      "- Call a Sentry operation returned by search_sentry_tools",
      "- Execute a tool by name using arguments that match its returned schema",
      ...
      "- Arguments are validated against the target tool's schema before execution.",
Does not applyOAuth 2.1 + PKCE Correctness (MCP Authorization)ATR-M0005Details

Why it got this result

The component exposes only stdio (packages/mcp-core/src/transports/stdio.ts). There is no remote authorization surface and no OAuth flow in this package; the credential is supplied by the host process.

Why it matters

A remote MCP endpoint without OAuth 2.1 authorization can let anyone who reaches it, or who obtains a static key, call every tool it serves. A misconfigured OAuth flow enables token theft and confused-deputy attacks.

Does not applyNo Authentication or Authorization Checks (General)ATR-M0010Details

Why it got this result

This rule concerns remote transports, and this package ships only a stdio adapter. The server is reached over stdio by the parent process, with no network listener, so the remote-listener authentication rule does not apply to this component.

Why it matters

Prevents unauthorized tool use and data access.

Evidence

requiredScopes: Scope[],
 ...
  return requiredScopes.every((scope) => expandedScopes.has(scope));
...
  if (!requiredScopes || requiredScopes.length === 0) {
  ...
  return hasRequiredScopes(grantedScopes, requiredScopes);
Does not applyOAuth Proxy Per-Client Consent (Confused Deputy Defense)ATR-M0011Details

Why it got this result

This package is not an OAuth proxy: it has no /authorize endpoint and no client registration. It is a stdio tool library that calls the Sentry API with the invoking user's credential, so no shared ambient credential is wielded for other callers.

Why it matters

Shared upstream client + dynamic MCP-client registration + no per-client consent enables attacker-registered MCP clients to receive bridge codes carrying victim upstream tokens — the documented MCP Confused Deputy attack.

Does not applyCORS / Origin / Host Validation (DNS Rebinding Defense)ATR-M0003Details

Why it got this result

The only transport in this package is stdio (src/transports/stdio.ts, StdioServerTransport). No HTTP/SSE listener exists, so there is no Origin/Host surface.

Why it matters

Prevents DNS rebinding/browser hijack of local or remote MCP.

Does not applyBinding to 0.0.0.0 Without Auth/TLSATR-M0004Details

Why it got this result

The component is stdio-only; it has no listen/bind call, Dockerfile or ingress.

Why it matters

LAN/Internet exposure enables remote exploitation.

Does not applyCORS Misuse: Wildcard with CredentialsATR-M0008Details

Why it got this result

The package has no HTTP server and sets no CORS headers (stdio only).

Why it matters

Prevents credentialed cross-origin abuse.

Does not applyTransport Security: TLS Everywhere (Remote)ATR-M0009Details

Why it got this result

The server transport is stdio only, so no remote MCP endpoint exists to secure. Outbound Sentry API calls are made through the host-configured Sentry host.

Why it matters

MitM can steal tokens and tamper responses.

Show its 80 tools
ToolIt can
add_issue_note
Add a note to a Sentry issue
Create or change data
add_team_to_project
Add a team to a project
Create or change dataManage identities and access
analyze_issue_with_seer
AI root cause analysis for an issue via Seer
Create or change dataSpend money or metered resources
create_alert_rule
Create an alert rule
Create or change dataChange settings
create_dsn
Create a project client key (DSN)
Create or change dataUse secrets and credentials
create_metric_monitor
Create a metric monitor
Create or change dataChange settings
create_monitor
Create a cron monitor
Create or change data
create_project
Create a project
Create or change data
create_team
Create a team
Create or change dataManage identities and access
create_uptime_monitor
Create an uptime monitor
Create or change dataChange settingsReach any network address
delete_alert_rule
Delete an alert rule
Delete or overwrite data
delete_metric_monitor
Delete a metric monitor
Delete or overwrite data
delete_monitor_environment
Delete a monitor environment
Delete or overwrite data
delete_uptime_monitor
Delete an uptime monitor
Delete or overwrite data
execute_sentry_tool
Execute any catalog tool discovered via search_sentry_tools (dispatcher; annotated destructive)
Create or change dataDelete or overwrite dataChange settings
find_alert_rules
List alert rules
Read data
find_dashboards
List dashboards
Read data
find_dropped_events
Find dropped events
Read data
find_dsns
List project client keys (DSNs)
Read dataUse secrets and credentials
find_metric_monitors
List metric monitors
Read data
find_monitors
List cron monitors
Read data
find_organizations
List organizations
Read data
find_projects
List projects
Read data
find_releases
List releases
Read data
find_teams
List teams
Read data
find_uptime_monitors
List uptime monitors
Read data
get_agent_conversation_details
Get agent conversation details
Read dataReturns untrusted content
get_ai_conversation_details
Deprecated alias for get_agent_conversation_details
Read dataReturns untrusted content
get_alert_options
Get alert rule options
Read data
get_alert_rule
Get alert rule details
Read data
get_dashboard_details
Get dashboard details
Read data
get_doc
Fetch a Sentry documentation page
Read dataReturns untrusted content
get_event_attachment
Download an event attachment
Read dataReturns untrusted content
get_event_stacktrace
Get event stacktrace
Read dataReturns untrusted content
get_issue_activity
Get issue activity
Read dataReturns untrusted content
get_issue_breadcrumbs
Get issue breadcrumbs
Read dataReturns untrusted content
get_issue_details
Get issue details
Read dataReturns untrusted content
get_issue_tag_values
Get issue tag values
Read dataReturns untrusted content
get_issue_user_reports
Get issue user reports
Read dataReturns untrusted content
get_latest_base_snapshot
Get the latest base snapshot for an app
Read data
get_metric_monitor_details
Get metric monitor details
Read data
get_monitor_details
Get cron monitor details
Read data
get_profile
Get a profile
Read data
get_profile_details
Get profile details
Read data
get_release_details
Get release details
Read data
get_replay_details
Get replay details
Read dataReturns untrusted content
get_sentry_mcp_info
Info about the Sentry MCP server
Read data
get_sentry_resource
Fetch a Sentry resource by URL or ID
Read dataReturns untrusted content
get_snapshot
Get a preprod snapshot diff summary
Read data
get_snapshot_image
Get a snapshot image
Read dataReturns untrusted content
get_span_details
Get span details
Read dataReturns untrusted content
get_trace_details
Get trace details
Read dataReturns untrusted content
get_uptime_monitor_details
Get uptime monitor details
Read data
link_issue
Link an issue to an external tracker
Create or change data
onboarding_status_update
Update onboarding status
Create or change data
remove_team_from_project
Remove a team from a project
Delete or overwrite dataManage identities and access
search_agent_conversations
Search agent conversations
Read dataReturns untrusted content
search_ai_conversations
Deprecated alias for search_agent_conversations
Read dataReturns untrusted content
search_attributes
Search available telemetry attributes
Read data
search_docs
Search Sentry documentation
Read dataReturns untrusted content
search_errors
Search error events
Read dataReturns untrusted content
search_events
Search events
Read dataReturns untrusted content
search_issue_events
Search events within an issue
Read dataReturns untrusted content
search_issues
Search grouped issues
Read dataReturns untrusted content
search_logs
Search logs
Read dataReturns untrusted content
search_metrics
Search metrics
Read data
search_profiles
Search profiles
Read data
search_replays
Search replays
Read dataReturns untrusted content
search_sentry_tools
Search available Sentry MCP catalog tools
Read data
search_traces
Search traces/spans
Read dataReturns untrusted content
static_tool
Read data
submit_feedback_to_sentry_team
Submit feedback about the MCP to the Sentry team
Create or change dataPost or message on your behalf
unlink_issue
Unlink an external issue
Create or change dataDelete or overwrite data
update_alert_rule
Update an alert rule
Create or change dataChange settingsDelete or overwrite data
update_dsn
Update a project client key (DSN)
Create or change dataUse secrets and credentialsChange settingsDelete or overwrite dataSpend money or metered resources
update_issue
Update issue status/assignment
Create or change dataDelete or overwrite data
update_metric_monitor
Update a metric monitor
Create or change dataChange settingsDelete or overwrite data
update_project
Update a project
Create or change dataChange settings
update_uptime_monitor
Update an uptime monitor
Create or change dataChange settingsReach any network addressDelete or overwrite data
whoami
Identify the authenticated user
Read data

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/plugins/sentry-mcpplugins/sentry-mcpNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

Plugin bundles a prose agent definition and a .mcp.json pointing to Sentry's hosted MCP server; its behavior (searching/managing Sentry resources) matches its description. No bundled scripts or undeclared egress.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

{
  "sentry": {
    "type": "http",
    "url": "https://mcp.sentry.dev/mcp"
  }
}
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The agent body is a workflow and tool-selection guide with no consent-suppression, disclosure-hiding or authority-override prose. It tells the agent to use the Sentry MCP tools instead of fetching Sentry URLs over raw HTTP, which delegates authentication to MCP and does not skip user approval. The bundle does not itself establish how the host gates MCP tool calls, so no consent gate is assumed as a repository control.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

1. Identify the user's intent and select the most appropriate tool by reading tool descriptions.
2. Pass Sentry URLs unchanged to `issueUrl` or `url` parameters — NEVER try to fetch Sentry URLs via HTTP directly, always use the MCP tools which handle authentication.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

allowedTools is an explicit enumerated list of Sentry MCP tools with no wildcard, matching the agent's stated purpose of searching, triaging and managing Sentry resources. It includes create, update and delete tools (e.g. delete_alert_rule, update_project), but these operate on Sentry resources within the described management role rather than on the shell, filesystem or ambient host control. The Claude and Cursor configs in the bundle declare the same remote server. Host approval behavior is external to the bundle and is not relied upon. Hardening: consider omitting destructive tools from the default allow-list.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

name: sentry-mcp
description: Sentry error tracking and performance monitoring agent. Use when the user asks about errors, exceptions, issues... Handles searching, analyzing, triaging, and managing Sentry resources including preprod snapshot inspection.
  the user asks about errors, exceptions, issues, stack traces, performance,
  traces, releases, snapshots, screenshots, visual regression, CI snapshot
  failures, preprod checks, agent conversations, or provides a Sentry URL
  (especially URLs containing sentry.io/preprod/snapshots/). Handles searching,
  analyzing, triaging, and managing Sentry resources including preprod snapshot
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

Codepoint scan of agent markdown found no concealment characters; no HTML comments, forged role tokens or host-directed injection.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

You are a Sentry expert. Investigate errors, analyze performance, and manage projects using the available MCP tools.
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

The plugin's own agent instructions and manifest ship in the bundle. The launch entry is a remote HTTP URL (not an npx/uvx unpinned package fetch), and no self-updating or rewriting of the manifest exists. Tool definitions are served by the publisher's hosted MCP server, inherent to remote MCP and covered by ATR-M rules.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

{
  "sentry": {
    "type": "http",
    "url": "https://mcp.sentry.dev/mcp"
  }
}
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The repository origin is https://github.com/getsentry/toolkit, the genuine product owner, which establishes publisher authority independently of the manifest's self-asserted author. The Claude (sentry-mcp) and Cursor (sentry) plugin names use canonical spelling with no spoofing prefixes or replacement installers.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

{
  "name": "sentry-mcp",
  "description": "Sentry error tracking and performance monitoring via MCP",
  "author": { "name": "Sentry" }
}
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

Agent instructions read no host credential stores or conversation context and direct no data to external sinks other than the declared Sentry MCP tools; no harvest step exists.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

Plugin ships a plugin.json, an agent markdown and a .mcp.json that declares a remote HTTP server URL (no command/args, no env assignments); no hooks, scripts, dotenv, endpoint-substituting env, or memory writes.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.
Skillskills-plugins/plugins/sentry-mcp-experimentalplugins/sentry-mcp-experimentalNone flagged

What stood out

  • docs-mcp (apps/docs-mcp/src/index.ts, access.ts, mcp.ts) exposes a remote HTTP MCP transport and /internal/docs with no real authentication: no OAuth 2.1/PKCE flow, only a hostname-based service-binding gate that trusts the platform to preserve the caller's internal URL rather than a verified credential, including on the localhost dev path.
  • docs-mcp has no Origin validation and uses a default permissive cors() configuration on /mcp and /internal/docs, leaving it open to cross-origin and DNS-rebinding style access.
  • Tool output returns third-party-authored content to the model without an untrusted envelope or sanitization; e.g. formatImageMetadata in packages/mcp-core/src/tools/support/snapshots/handlers.ts embeds uploader-controlled display_name and description directly in tool text (prompt injection).
  • Supply chain: GitHub workflows (.github/workflows/eval.yml, token-cost.yml) use mutable action tags rather than pinned commit SHAs; a moved upstream tag would run different code in CI jobs holding repository tokens.
  • Missing input validation: ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts have no maximum lengths or cursor character pattern, so find_projects passes oversized or malformed values to listProjects.
  • DoS exposure in image fetching: fetchImageByUrl (packages/mcp-core/src/api-client/client.ts) has no response size cap and the abort timer does not cover the full body read, affecting get-snapshot-image and snapshot handlers.
  • Logging hygiene: docs-mcp (apps/docs-mcp/src/mcp.ts, index.ts) records full MCP inputs/outputs and default PII in telemetry without redaction before transmission.
  • Terminal output injection: untrusted values such as issue titles are not stripped of ESC/control bytes before ANSI styling in CLI formatters (packages/cli/src/commands/issue/view.ts, lib/formatters/human.ts, packages/toolkit-core/src/markdown.ts).

Skill rules 0 of 8 flagged

6 rules passed · 2 did not applyShow
PassedCapability Transparency: Declared Purpose vs. Actual Behavior (Shadow Features)ATR-P0001Details

Why it got this result

The plugin is a manifest, an agent markdown file and an MCP connection to the first-party Sentry endpoint. It bundles no scripts and has no undisclosed network, exec or credential behaviour. The agent's description matches the tools it is allowed.

Why it matters

Install-time trust is granted from the artifact's self-description, so any bundled capability the description does not disclose -- network egress, credential reads, or subprocess execution -- runs as an unconsented backdoor with the host agent's full privileges.

Evidence

{
  "mcpServers": {
    "sentry": {
      "type": "http",
      "url": "https://mcp.sentry.dev/mcp?experimental=1"
    }
  }
}
PassedConsent Suppression and Guardrail / Human-in-the-Loop Subversion via Trusted InstructionsATR-P0002Details

Why it got this result

The agent instructions are workflow guidance with no 'don't ask' or 'work silently' prose. The only imperative restricts the agent to the MCP tools rather than raw HTTP. Hardening note: allowedTools lists destructive tools such as delete_alert_rule; whether those auto-run depends on the host's permission model, and the prose does not suppress confirmation.

Why it matters

Because a skill/plugin instruction body is ingested as trusted first-party context, consent- or oversight-suppressing prose inherits the host's full authority and turns a passive package into a self-authorizing backdoor that strips the human-in-the-loop control from every action it can reach.

Evidence

2. Pass Sentry URLs unchanged to `issueUrl` or `url` parameters — NEVER try to fetch Sentry URLs via HTTP directly, always use the MCP tools which handle authentication.
PassedOver-Declared / Over-Privileged Artifact Manifest ScopeATR-P0005Details

Why it got this result

The agent's allowedTools is an explicit enumerated list of Sentry MCP tools (no '*' wildcard) and matches the documented purpose of 'searching, analyzing, triaging, and managing Sentry resources'. It includes mutating and delete tools, but these are intrinsic to the stated management function. There is no shell, filesystem or network tool class beyond the Sentry MCP.

Why it matters

An artifact that self-declares host privilege far beyond its stated function -- or hides executable reach inside a passive-looking package -- gives a single install a blast radius the operator never scoped, so any later mistake, compromise, or misconfiguration detonates across resources the artifact never needed.

Evidence

mcpServers:
  - sentry
allowedTools:
  - add_issue_note
  - add_team_to_project
  - analyze_issue_with_seer
  - create_alert_rule
  ...
  - delete_alert_rule
PassedInjection and Concealed Directives in Skill/Plugin Manifest and Instruction TextATR-P0006Details

Why it got this result

A codepoint scan found no zero-width, bidi or Tag characters in the plugin files. There are no forged role tokens or host-override directives. The agent body is task guidance.

Why it matters

A skill or plugin's always-loaded manifest and instruction carriers (frontmatter/body, `description_for_model`, Gemini `FunctionDeclaration.description` / Vertex AI extension `manifest` / ADK agent `instruction`, tool docstrings, JSON-Schema descriptions) are concatenated into the host agent prompt at operator privilege, so a directive that is hidden with non-printing codepoints, disguised with a homoglyph, forged with role-control tokens, or masked from the rendered view silently commandeers model behavior toward attacker-chosen actions across every tool the artifact serves -- with no code execution required and nothing visible to a reviewer reading the rendered text.

Evidence

## Workflow

1. Identify the user's intent and select the most appropriate tool by reading tool descriptions.
2. Pass Sentry URLs unchanged to `issueUrl` or `url` parameters — NEVER try to fetch ...
PassedSkill/Plugin Package Provenance, Integrity, and Post-Approval Mutation (Rug-Pull)ATR-P0007Details

Why it got this result

The agent instructions and the allowedTools list ship inside the bundle and are not live-fetched. The MCP entry is a documented first-party remote URL, not an unpinned npx/uvx launch package. The tools the remote server serves are the publisher's own backend and follow the host's normal MCP flow. This is a trust-in-publisher note, not a skill-instruction fetch.

Why it matters

A skill/plugin reviewed once and then trusted indefinitely becomes a rug-pull vector when its OWN model-facing instruction body, declared scope, or manifest -- or the package its launch entry runs -- can be live-swapped or silently mutated after approval -- without an approval-time digest that is re-compared on load and re-consent on any change, a publisher (or an attacker who compromises the distribution channel) can rewrite the artifact's trusted instructions or widen its tool scope under authority the host already granted, turning an approved package into an exfiltration or unauthorized-action backdoor.

Evidence

{
  "sentry": {
    "type": "http",
    "url": "https://mcp.sentry.dev/mcp?experimental=1"
  }
}
PassedSkill/Plugin Impersonation, Typosquatting & Publisher SpoofingATR-P0008Details

Why it got this result

The plugin is published from the getsentry org repo, with author 'Sentry' in the manifest, and no impersonation prose. The brand owner is the publisher.

Why it matters

Impersonation is the entry point of the agentic supply chain (OWASP Top 10 for Agentic Applications ASI04 / MITRE ATT&CK T1195): a skill or plugin installed on the strength of a familiar name inherits the host agent's execution context before any payload runs. The deception front-runs trust, so catching the identity lie breaks the chain at the cheapest point -- and it is independent of whether the current code is benign, because the same trusted-looking name can ship a payload in the next version.

Evidence

{
  "name": "sentry-mcp-experimental",
  "description": "Sentry MCP with experimental features enabled",
  "author": { "name": "Sentry" }
}
Does not applyHost-Credential and Conversation-Context Harvesting and ExfiltrationATR-P0003Details

Why it got this result

The plugin contains no credential-store reads, env enumeration or conversation-context routing. Authentication is handled by the host's MCP connection.

Why it matters

A skill or plugin that couples a host-credential or live-conversation read with an outbound channel turns an ostensibly benign helper into an autonomous exfiltration agent, leaking long-lived secrets, session tokens, and confidential prompt context to an attacker on every invocation across every tool the artifact serves.

Does not applyFormat-Native Out-of-Band Execution and Host Control-Plane PersistenceATR-P0004Details

Why it got this result

The plugin ships only JSON manifests, an MCP remote URL and an agent markdown file. It has no hooks, scripts, exec-enabling env, or base-URL substitution. The remote MCP entry is a plain https URL to the first-party host.

Why it matters

Format-native execution and control-plane writes bypass the model's reasoning loop, converting a single install approval into durable, self-reinjecting code execution that can MITM the host agent's own model endpoint and silently subvert every subsequent tool call.

Before you put it to work

  1. Require a verified credential (shared secret header or signed service-to-service token) in the docs-mcp access check for /mcp and /internal/docs, including the localhost path, instead of hostname alone; front with OAuth 2.1 (resource metadata, PKCE) if exposed beyond the service binding.
  2. Add explicit Origin allowlist validation to docs-mcp /mcp and /internal/docs and replace default cors() with one restricted to trusted origins.
  3. Wrap third-party content in a delimited untrusted block, strip control and role-marker sequences, and flag content tools as untrusted-output.
  4. Pin every GitHub Action to a full commit SHA (as deploy.yml and pr-risk-jev.yml already do), including eval.yml and token-cost.yml.
  5. Add max lengths and a cursor character pattern to ParamSearchQuery and ParamCursor in packages/mcp-core/src/schema.ts so find_projects rejects bad values early.
  6. In fetchImageByUrl, enforce a max response size via Content-Length check and byte-capped streaming, and keep the abort timer active until the body is fully read before base64 conversion.
  7. In apps/docs-mcp, disable recording of full MCP inputs/outputs or apply a redaction scrubber (e.g. beforeSend) before telemetry is sent, and review the default-PII setting in index.ts.
  8. Strip ESC and other terminal control bytes from untrusted upstream text before applying trusted ANSI styling in CLI formatters, including the interactive rendering path.

Repository-wide rules

Checked once for the whole repository: dependencies, secrets, inputs, networking, CI and more. 6 of 17 flagged.

Could not determineUnrestricted Network Fetch / SSRFATR-C0001Details

Why it got this result

SentryApiService.fetchImageByUrl does a bare fetch() of any https:// URL taken from Sentry snapshot API data (image_url / diff_image_url), with no host allowlist, private-IP filter, timeout or size cap. It is https-only and forwards no credentials. Content sniffing can accept image bytes even when the response lacks an image/* header. The hosted Wrangler config sets global_fetch_strictly_public, a platform mitigation for the Worker, but the stdio runtime has no such guarantee. The URL source is the upstream snapshot response, not the exposed imageIdentifier argument, and the repository does not establish that an attacker can write those URL fields, so a full source-to-sink flow is not proven. All other outbound calls go to a configured Sentry host, OAuth endpoints or fixed LLM providers.

Why it matters

Enables internal pivoting and data exfiltration.

Evidence

async fetchImageByUrl(
    imageUrl: string,
  ): Promise<{ blob: Blob; contentType: string }> {
    const response = imageUrl.startsWith("https://")
      ? await fetch(imageUrl)
      : await this.request(
7 rules passed · 3 did not applyShow
PassedRuntime Command Injection via Process ExecutionATR-C0003Details

Why it got this result

Process spawns in the CLI use argv-style spawn/execFileSync with fixed executables. The semgrep spawn-shell-true hits in packages/cli/src/lib/upgrade.ts use shell only on win32, with a fixed command (brew) or a semver-validated version (semverValid(version) === version). monitor run / local run execute the command the operator typed on their own CLI, which is the declared purpose and not an attacker-controlled path. The MCP server (mcp-core, Cloudflare Worker) has no child_process use. No eval or Function on external input was found. CI expressions are out of scope for this rule.

Why it matters

Unsanitized command construction in shipped runtime code can give a remote attacker arbitrary code execution with the application's privileges.

Evidence

const proc = spawn(command, args, {
      stdio: ["ignore", "pipe", "pipe"],
      shell: process.platform === "win32",
    });
PassedAuthentication Bypass via Caller-Controlled Trust FlagATR-C0016Details

Why it got this result

Remote auth goes through the Cloudflare OAuth provider, or a Sentry-Bearer token that is passed upstream so Sentry itself verifies it. No request boolean grants a session or scope.

Why it matters

A caller-toggleable "trusted path" grants unauthenticated or privileged access while a real credential check sits visibly in the code, so scanners that look for a missing check pass it and human reviewers see the check and stop reading. The result is full authentication bypass from an anonymous request.

Evidence

 * - OAuth requests extract auth props from ExecutionContext (set by OAuth provider)
 * - Sentry-Bearer requests pass an explicit upstream token directly
 * Direct Sentry-Bearer mode intentionally does not validate, store, refresh,
 * or revoke the upstream token. It only supplies that token to ServerContext.
PassedWeaponization: Malicious-Purpose Artifact (Offensive Capability by Design)ATR-R0001Details

Why it got this result

The product is the Sentry monitoring CLI and MCP server. The YARA hits were reviewed and are benign: the PowerShell installer validates versions, and its GitHub-download fallback explicitly warns that the download is not checksum-verified, which is a documented limitation and not malicious purpose. The project delete docs describe a documented --yes confirmation bypass flag for explicit administration, not covert attack tooling.

Why it matters

An artifact whose purpose is to weaponize the agent -- to produce malware, steal credentials, evade defenses, or run attacks at scale -- is dangerous regardless of how securely it is built; posture controls (auth, input validation, least privilege) cannot make a weapon safe, which is why a confirmed weaponization finding caps the trust verdict to `blocked` rather than being averaged against a good posture score. Catching malicious PURPOSE is a distinct safety control, and it must be conservative because offensive capability is dual-use: legitimate pentest, CTF, defensive, and research work must abstain (UNKNOWN), never block -- unless the artifact itself hides from the people who install, run or audit it.

Evidence

"Permanently delete a Sentry project. This action cannot be undone.\n\n" +
      "Requires explicit target — auto-detection is disabled for safety.",
    examples: [
      {
        description: "Delete a project (prompts for confirmation)",
        command: "sentry project delete my-org/old-project",
      },
      {
        description: "Delete without confirmation",
        command: "sentry project delete my-org/old-project --yes",
PassedStatic SecretsATR-C0002Details

Why it got this result

Secrets are read from env vars or runtime config. The committed .env.example files hold only empty or placeholder values. The OAuth client ID in oauth.ts is a documented public device-flow ID. The semgrep hit at sentryclirc.ts:367 is a log.debug that records only the config file name and source of the token, never the token value. The token is copied from the user's own .sentryclirc into the process env at runtime. Gitleaks hits are tests, fixtures and docs.

Why it matters

Plaintext secrets are easily exfiltrated.

Evidence

Code not shown: this line may hold a secret.

Show 2 more locations

Code not shown: this line may hold a secret.

Code not shown: this line may hold a secret.

+1 more location in the finding JSON

PassedInsecure DeserializationATR-C0009Details

Why it got this result

Only JSON.parse of plain data is used (for example a base64 pagination cursor), with no code-reviving deserializers.

Why it matters

Can lead to arbitrary code execution.

Evidence

    parsed = JSON.parse(atob(cursor.slice(ACCOUNTS_CURSOR_PREFIX.length)));
PassedCI/CD Workflow Script InjectionATR-C0014Details

Why it got this result

Untrusted values reach scripts through env indirection (PR_NUMBER, SOURCE_RUN_ID, EXPECTED_SHA, PRODUCT). pr-risk-jev.yml runs under pull_request_target with pinned trusted action code and PR_NUMBER passed in env, and it never checks out or executes PR code. The workflow_run deploys require a successful same-repository push on main, compare the tested SHA to the current main head (an equality check, not an ancestry check) before checkout, pass the SHA as quoted env data, and use persist-credentials: false. The expression interpolations in token-cost.yml derive from the measurement job's numeric metrics and constant comparison symbols, not attacker-controlled event text, so they confer no privilege gain. cli-build.yml's run line interpolates a value from a literal matrix allowlist. No complete attacker-to-script chain was established.

Why it matters

CI/CD script injection can execute attacker-controlled commands with workflow tokens, repository secrets, trusted-runner access, artifact or cache authority, and release or deployment privileges.

Evidence

- uses: actions/checkout@11d5960... # v4.4.0
        with:
          ref: ${{ github.event.workflow_run.head_sha }}
          persist-credentials: false
      - name: Require tested revision on main
        env:
          EXPECTED_SHA: ${{ github.event.workflow_run.head_sha }}
PassedDenial of Wallet / Cost ControlsATR-C0012Details

Why it got this result

The hosted service has Cloudflare rate-limit bindings, and the embedded LLM agent calls use provider fallback and capped retries with backoff (callEmbeddedAgent.ts, fetch-utils.ts). The risk of unbounded billable calls is bounded.

Why it matters

Prevents cost-based disruption.

Evidence

import type { RateLimit } from "@cloudflare/workers-types";
export const MCP_RATE_LIMIT_EXCEEDED_MESSAGE =
  "Rate limit exceeded. Please wait before trying again.";
Does not applyPath Traversal & Symlink Bypass (Filesystem Tools)ATR-C0011Details

Why it got this result

The MCP server exposes only Sentry API tools and no filesystem tools. CLI path arguments are the local operator's own, in a local CLI context, with no caller-supplied path served over a remote surface.

Why it matters

Traversal exposes arbitrary host files; can lead to code execution.

Does not applyArchive Extraction Path Escape (zip-slip / tar-slip)ATR-C0015Details

Why it got this result

Runtime ZIP handling exists: DIF unzipSync in lib/dif/zip.ts returns in-memory buffers with synthetic diagnostic paths, and the build ZIP listing in lib/build/index.ts never decompresses members. Neither writes to a destination derived from a member name, so there is no zip-slip filesystem sink. The installers download release binaries rather than extracting externally supplied archives.

Why it matters

A crafted archive can name members such as ../../.ssh/authorized_keys. Code that writes members by name without checking the destination writes attacker-chosen files anywhere the process can, including startup and credential locations.

Does not applyContainer/Runtime HardeningATR-C0006Details

Why it got this result

The repo ships no Dockerfile, compose file or Kubernetes manifest. Deployment is to Cloudflare Workers (wrangler.jsonc) and as npm/standalone binaries.

Why it matters

Reduces blast radius of exploits.

From build to runtime

You know what it is. Now see what it does.

A scan helps you understand what you’re building with. When your agent runs, BlueRock shows you what actually happens across tools and systems. Follow actions in context, understand behavior as it unfolds and add controls when you need them.

BUILDRegistry + scan
RUNSee actual execution
OPERATEObserve, control, optimize

Results reflect the code at the commit and date shown.