# Magpie Security Issue Import

> |

- **Type:** Skill
- **Install:** `agentstack add skill-apache-magpie-security-issue-import`
- **Verified:** Pending review
- **Seller:** [apache](https://agentstack.voostack.com/s/apache)
- **Installs:** 0
- **Category:** [Security](https://agentstack.voostack.com/c/security)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [apache](https://github.com/apache)
- **Source:** https://github.com/apache/magpie/tree/main/skills/security-issue-import
- **Website:** https://magpie.apache.org/

## Install

```sh
agentstack add skill-apache-magpie-security-issue-import
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# security-issue-import

This skill is the **on-ramp** of the security-issue handling process.
It converts an inbound `` email thread into
an `` tracking issue that follows the repo's issue
template, then drafts the receipt-of-confirmation reply to the reporter.

It never sends email. It never creates a tracker for a candidate the
user has explicitly rejected. It never assumes a report is valid —
the validity / invalid / CVE-worthy decision still happens later in
the discussion on the created tracker (Step 3 of
[`README.md`](../../README.md)).

**Golden rule — propose, then default to import.** Every import this
skill performs is a *proposal* that lists the candidate emails, the
extracted fields, and the draft confirmation reply. The user's
default disposition for any `Report` or forwarder-relayed
candidate (the latter classified by the optional
[`security-issue-import-via-forwarder`](../security-issue-import-via-forwarder/SKILL.md)
sub-skill when `forwarders.enabled` is non-empty) is
**"import as a new tracker landing in `Needs triage`"**;
the user only has to type back when they want to *deviate* from that
default — `skip NN` to reject a candidate upfront with no reply, or
`NN:reject-with-canned ` to reject upfront *and* draft a
specific canned negative-assessment / out-of-scope reply. A bare
`all` (or no reply at all to the proposal — the user typing
*"go"*, *"proceed"*, *"yes, all"*) means *"import every
non-rejected candidate as proposed"*. The skill must still surface
each candidate one-by-one in the proposal so the user can scan and
override if needed; what the skill must *not* do is sit on a report
waiting for an explicit per-candidate green light. The bias is
toward landing trackers — a wrongly-imported report is cheap to
close at Step 5 / 6 of the handling process; a wrongly-skipped one
gets buried in the inbox and the reporter is left without a
disposition.

**Golden rule — rejection means no tracker, ever.** When the user
rejects a candidate upfront — any of `skip NN`,
`NN:reject-with-canned `, an explicit *"reject 1"*,
*"mark 1 invalid"*, *"don't import 1"*, or a `cancel` / `none` /
*"hold off"* on the whole proposal — the skill **must not** create
a tracker for that candidate. This holds even when the user also
asks for a canned reply to be drafted: the draft is a courtesy to
the reporter, the absence of a tracker is the disposition. There is
no "create the tracker so the team can close it as invalid later"
path; if the team has decided pre-triage that the report is
invalid, the audit trail lives on the Gmail thread and on the
`canned-responses.md` precedent, not in a tracker that exists only
to be closed. A tracker is created **only** when the candidate is
imported as a real `Report` (or a forwarder-relayed candidate
classified by the
[`security-issue-import-via-forwarder`](../security-issue-import-via-forwarder/SKILL.md)
sub-skill) for triage.

Non-import candidate classes (`automated-scanner`,
`consolidated-multi-issue`, `media-request`, `spam`,
`cross-thread-followup`, `cve-tool-bookkeeping`) keep the original
"propose first, apply only on explicit confirm" rule — those never
default to a tracker.

**Golden rule — confidentiality.** The inbound thread on
`` is private. The skill may paste the
email body verbatim into the created `` tracking
issue (that repo is also private). It must **never** paste the
report content into a public surface — not into ``, not
into a public GHSA, not into any comment on a public repo. The same
confidentiality rule documented in the "Confidentiality of
``" section of [`AGENTS.md`](../../AGENTS.md)
applies in full.

**Golden rule — every `` / `` reference is
clickable in the surface it lands on.** Whenever this skill emits
a reference to a tracker issue, PR, or comment — the proposal
shown to the user before import, the created tracker issue body
(observed-state dump, sibling-tracker cross-links, prior-rejection
cross-links, fix-already-public PR pointers), the receipt-of-
confirmation draft email reply, the recap output — the reference
must be one click away in whatever surface it lands on:

- **On markdown surfaces** (the created tracker issue body, the
  draft email reply destined for the `` thread,
  any markdown-rendered cross-link list): use the markdown link
  form per
  [`AGENTS.md` § *Linking tracker issues and PRs*](../../AGENTS.md#linking-tracker-issues-and-prs):
  - **Sibling `` issue**: `[#NNN](https://github.com//issues/NNN)`
  - **Public `` PR** (e.g. fix-already-public match):
    `[#NNN](https://github.com//pull/NNN)`
  - **Comment**: link to the `#issuecomment-` anchor.

- **On terminal surfaces** (the proposal shown to the user before
  import, the recap output): wrap the visible short form
  (`#NNN`, `#NNN`) in **OSC 8 hyperlink escape
  sequences** (`\e]8;;\e\\\e]8;;\e\\`) so modern
  terminals (iTerm2, Kitty, GNOME Terminal, WezTerm, Windows
  Terminal, …) render the short text as clickable. Where OSC 8
  is unsupported (CI logs, dumb terminals), fall back to printing
  the bare URL on the same line after the number.

Bare `#NNN` with no link wrapper of any kind is never acceptable.
The created tracker issue is read by the security team who drill
into the cross-links to assess; the draft email reply lands on
`` where the reporter needs the references to be
one click away. Both surfaces are private, but `` URLs
themselves are public-safe per the
[Confidentiality of ``](../../AGENTS.md#confidentiality-of-the-tracker-repository)
rule — what stays private is the *contents* the link points at.

**Self-check before posting any draft email or creating any
tracker issue**: grep the body for bare `#\d+` / `#\d+`
tokens that aren't already inside a markdown link or an OSC 8
wrapper, and convert any match.

---

## Adopter overrides

Before running the default behaviour documented
below, this skill consults
[`.apache-magpie-overrides/security-issue-import.md`](../../docs/setup/agentic-overrides.md)
in the adopter repo if it exists, and applies any
agent-readable overrides it finds. See
[`docs/setup/agentic-overrides.md`](../../docs/setup/agentic-overrides.md)
for the contract — what overrides may contain, hard
rules, the reconciliation flow on framework upgrade,
upstreaming guidance.

**Hard rule**: agents NEVER modify the snapshot under
`/.apache-magpie/`. Local modifications
go in the override file. Framework changes go via PR
to `apache/magpie`.

---

## Snapshot drift

Also at the top of every run, this skill compares the
gitignored `.apache-magpie.local.lock` (per-machine
fetch) against the committed `.apache-magpie.lock`
(the project pin). On mismatch the skill surfaces the
gap and proposes
[`/magpie-setup upgrade`](../setup/upgrade.md).
The proposal is non-blocking — the user may defer if
they want to run with the local snapshot for now. See
[`docs/setup/install-recipes.md` § Subsequent runs and drift detection](../../docs/setup/install-recipes.md#subsequent-runs-and-drift-detection)
for the full flow.

Drift severity:

- **method or URL differ** → ✗ full re-install needed.
- **ref differs** (project bumped tag, or `git-branch`
  local is behind upstream tip) → ⚠ sync needed.
- **`svn-zip` SHA-512 mismatches the committed
  anchor** → ✗ security-flagged; investigate before
  upgrading.

---
## Prerequisites

Before running, the skill needs:

- **At least one configured mail-source backend** per
  [`/project.md → Mail sources`](../..//project.md#mail-sources).
  The skill treats every backend the same way — through the
  abstract operations defined in
  [`tools/mail-source/contract.md`](../../tools/mail-source/contract.md)
  (`list_recent_threads`, `read_thread`, `list_drafts`,
  `list_sent_since`, `create_draft`, `thread_url`). Reference
  adapters: [`gmail`](../../tools/gmail/tool.md) (full
  read+write), [`ponymail`](../../tools/ponymail/tool.md)
  (read-only ASF archive),
  [`imap`](../../tools/mail-source/imap/README.md) (stub),
  [`mbox`](../../tools/mail-source/mbox/README.md) (read-only
  offline archive — stub). To **discover new reports** the
  configured backends must collectively cover
  `list_recent_threads` + `read_thread`; to **draft the
  receipt-of-confirmation reply in Step 7** they must
  additionally cover `create_draft`. If no available backend
  covers `create_draft`, Step 7 surfaces a one-line *"no draft
  backend available"* note and the user composes the reply by
  hand.
- **`gh` CLI authenticated** (`gh auth status` returns OK) with
  collaborator access to ``. The skill calls
  `gh issue create` and `gh search issues` directly.

See
[Prerequisites for running the agent skills](../../docs/prerequisites.md#prerequisites-for-running-the-agent-skills)
in `docs/prerequisites.md` for the overall setup.

---

## Step 0 — Pre-flight check

Before touching any candidate thread, verify:

1. **Mail-source backends from `/project.md →
   Mail sources` are available.** For each declared backend, run
   the backend's trivial health probe (per its adapter doc —
   Gmail: `mcp__claude_ai_Gmail__search_threads` with `pageSize:
   1`; Ponymail: `mcp__ponymail__auth_status()`; IMAP: a
   `CAPABILITY` against the configured host; mbox: a `stat` on
   the archive path) and record the result in the skill's
   observed-state bag. Apply the
   [contract's resolution rule](../../tools/mail-source/contract.md#resolution-rule--which-backend-runs-an-operation)
   to figure out which backend serves which op for this run.

   * **`mandatory: yes` backend unavailable** → **stop
     immediately**. Surface *"mandatory mail-source backend
     `` unavailable: ``; run aborted"*. The user
     fixes the auth / connection and re-invokes.
   * **`mandatory: no` backend unavailable** → continue with the
     remaining backends. If the resolution then leaves an
     operation with no provider (e.g. no available backend
     supports `create_draft`), the skill records *"no ``
     backend available"* in the observed-state bag and the
     relevant downstream step omits that proposal with a clear
     hand-back to the user.
   * **Every declared backend healthy** → proceed; the
     observed-state bag records one provider per op so every
     dispatch later is unambiguous.
2. **`gh` is authenticated and has access.** Run
   `gh api repos/ --jq .name`; if it errors
   (401, 403, 404), stop and tell the user to log in with
   `gh auth login` or get added to ``.
3. **(Reference-adopter guidance.)** The reference adopter
   lists `gmail` as primary `mandatory: yes` and —
   per the ASF default — `ponymail` as `mandatory: yes` too
   (`fallback` role for drafts, since PonyMail is read-only). So
   for the reference flow **both** backends are pre-flight
   prerequisites: a Gmail-MCP failure stops the run (drafts have no
   home), and a PonyMail-MCP miss — not registered, or registered
   but unauthenticated for the private `` archive —
   stops it too, per item 1's `mandatory: yes` rule. Gmail handles
   reads of just-arrived inbound mail and all draft creation;
   PonyMail handles archive lookups (and is the primary read path
   when authenticated). Adopters whose `Mail sources` table sets
   `ponymail` to `mandatory: no` get the old degrade-quietly
   behaviour; the step-by-step references to "Gmail" below should
   be read as "the backend the resolution rule picked for the
   relevant op".
4. **Privacy-LLM contract.** This skill reads ``
   bodies that may contain third-party PII the reporter
   discloses about other people. Run the gate-check first —
   non-zero exit is a hard stop:

   ```bash
   uv run --project /tools/privacy-llm/checker \
     privacy-llm-check
   ```

   The checker auto-locates `/privacy-llm.md`
   (template at
   [`projects/_template/privacy-llm.md`](../../projects/_template/privacy-llm.md))
   and verifies every entry in *Currently configured LLM stack*
   is approved per
   [`tools/privacy-llm/models.md`](../../tools/privacy-llm/models.md#the-pre-flight-check).
   In addition, verify:
   - `~/.config/apache-magpie/` is writable (the redactor's
     mapping file lives there);
   - the configured collaborator source is reachable via
     `gh api` (default: `` from `project.md`);
   - the redaction-tuning knobs (collaborator exemption,
     enabled field types) are loaded into the skill's
     observed-state bag — they apply at filter-time below.

   Each subsequent body fetch in Steps 4 / 7 / 7g (template-
   field extraction, draft assembly, recap) follows the
   redact-after-fetch protocol in
   [`tools/privacy-llm/wiring.md`](../../tools/privacy-llm/wiring.md#redact-after-fetch-protocol);
   the receipt-of-confirmation draft assembly follows the
   [reveal-before-send protocol](../../tools/privacy-llm/wiring.md#reveal-before-send-protocol)
   when (and only when) the draft references a third-party
   identifier.

5. **Disclosure governance from `/security-intake-config.md`.**
   If the file exists, read the `disclosure_governance` block and load these
   two keys into the observed-state bag for use in Step 7:

   - `reporter_acknowledgement_model` — `manual` | `auto` | `none`. Controls
     whether and how the receipt-of-confirmation reply is drafted (Step 7.4).
   - `window_days` — integer; the CVD window in calendar days, used as the
     disclosure deadline hint when composing the acknowledgement draft.

   If the file does not exist or the `disclosure_governance` block is absent,
   silently default to `reporter_acknowledgement_model: manual` and
   `window_days: 90`. A missing file is **not** a stop condition — adopters
   who have not yet created this config receive the same ASF defaults the
   skill has always applied.

If a `mandatory: yes` mail-source backend or the `gh` check fails,
do **not** proceed — the skill would fail mid-flow otherwise,
leaving half-built state (a draft on the wrong thread, or a tracker
with no receipt reply). Fail fast instead. `mandatory: no` backends
degrade quietly per the contract's resolution rule. A privacy-llm
pre-flight failure is also a hard stop — the redactor's mapping
store and the collaborator-source lookup are both load-bearing for
every subsequent body read.

---

## Inputs

Before running, resolve the user's selector into a concrete set of
candidate Gmail threads:

| Selector | Resolves to |
|---|---|
| `import new` (default) | every security@ thread received in the last **14 days** that has not yet been imported as an  issue and has not already been answered-and-closed on-thread |
| `import since:YYYY-MM-DD` | every security@ thread received since the given date that is not yet imported |
| `import thread:` | the single Gmail thread with that `threadId` — useful for re-importing after a manual discard, or for picking up a single message the automatic scan missed |
| `import last 30d` / `import all` / `import last Nd` (explicit request only) | a wider sweep — use when the skill has not been run in a while or the user is doing a backlog catch-up. The `all` alias spans `disclosure_governance.window_days` days (default 90) from `/security-intake-config.md`. |

If the user supplies no selector, default to `import new` (14-day window).

**Why the default is 14 days.** Most reports that land on `security@`
fall into one of three steady-state buckets: (a) imported as a tracker
within days of arrival, (b) answered on-thread with a canned negative
response that the reporter accepts silently, or (c) obvious spam the
triager ignores. None of those need a second look past 14 days. Widening
the default window past two weeks would keep re-surfacing the same
already-handled threads every sync run, which is noise. The user can
always pass `import last 30d` or `import all` explicitly when a deeper
sweep is genuinely warranted (e.g. after a long quiet period, or during
a backlog audit).

---

## Step 1 — List candidate threads from Gmail

Search `` for inbound reports, excluding the
tooling / GitHub-notification / mailing-list chatter that isn't a
report:

Use the canonical candidate-l

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [apache](https://github.com/apache)
- **Source:** [apache/magpie](https://github.com/apache/magpie)
- **License:** Apache-2.0
- **Homepage:** https://magpie.apache.org/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-apache-magpie-security-issue-import
- Seller: https://agentstack.voostack.com/s/apache
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
