Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions plugins/magpie-security/skills/issue-sync/apply-and-push.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ Capture the printed URL — it deep-links to the `## CVE JSON — paste-ready fo

**When the operator's machine has a valid authenticated session** for the adapter named in `cve_authority.tool`
(one-time setup per `tools/<cve-tool>/README.md`; for Vulnogram,
`uv run --project <framework>/tools/cve-tool-vulnogram/oauth-api vulnogram-api-setup`, see
`uv run --project <framework>/tools/cve-tool-vulnogram/oauth-api vulnogram-api-setup --pmc <project>`, see
[`tools/cve-tool-vulnogram/oauth-api/README.md`](../../../../tools/cve-tool-vulnogram/oauth-api/README.md)),
**sync pushes the JSON to the record directly** through the adapter's `push_update(cve_id, fields, state_transition=None)` method
(per [`tools/cve-tool/README.md`](../../../../tools/cve-tool/README.md))
Expand Down Expand Up @@ -461,7 +461,7 @@ the four gates that need judgement (trigger conditions, the incomplete-fix claus

- **`valid`** → proceed to step 3.
- **`expired`** → skip the push and surface a one-line reminder in the Step 6 recap:
*"CVE-tool authenticated session expired — re-run the adapter's setup entrypoint (for the Vulnogram adapter, `vulnogram-api-setup`) to restore automatic push; using manual-paste hand-off this run."*
*"CVE-tool authenticated session expired — re-run the adapter's setup entrypoint (for the Vulnogram adapter, `vulnogram-api-setup --pmc <project>`) to restore automatic push; using manual-paste hand-off this run."*
Use the manual-paste hand-off variant for any 5c comment work below.
- **`not-configured`** → skip the push;
the manual-paste hand-off (via the `cve_authority.source_tab_url_template` link) still works.
Expand Down
4 changes: 2 additions & 2 deletions plugins/magpie-security/skills/issue-sync/gather.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ check whether a CVE reviewer has left comments on the record.

**Read the record's comments directly.** When the CVE-tool adapter exposes an authenticated record read —
for the ASF default, [`vulnogram-api-record-fetch`](../../../../tools/cve-tool-vulnogram/oauth-api/README.md),
on the Bearer token stored by `vulnogram-api-setup` —
on the Bearer token stored by `vulnogram-api-setup --pmc <project>` —
the reviewer comments are a **first-class field on the record** and that read is the primary signal path:

```bash
Expand All @@ -332,7 +332,7 @@ Run it for **every** tracker with an allocated CVE, not only those in `REVIEW`
reviewers comment on records that have moved on, and a comment on a `PUBLIC` record still needs an answer.

> **Which records to poll.** One cheap, read-only fetch per tracker with an allocated CVE; it needs a live token.
> On an expired-token error, re-run `vulnogram-api-setup` before continuing; if that is not possible this run,
> On an expired-token error, re-run `vulnogram-api-setup --pmc <project>` before continuing; if that is not possible this run,
> fall back to the mail path below and flag the gap in Step 2c rather than reporting *"no reviewer comments"* —
> a failed read is not an empty result.

Expand Down
7 changes: 4 additions & 3 deletions tools/cve-tool-vulnogram/oauth-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Console scripts:

| Console script | Purpose |
|---|---|
| `vulnogram-api-setup` | Interactive token capture — open `/users/token` in a regular browser (logging in there, MFA included, if needed), copy a token, paste it back into the script. |
| `vulnogram-api-setup` | Interactive token capture — approved in your browser via `--pmc <project>` (recommended) or copied from `/users/token` and pasted back. |
| `vulnogram-api-record-update` | POST a CVE JSON to `/cve5/<CVE-ID>` (the upsert endpoint). Replaces the release-manager copy-paste-into-`#source` step. |
| `vulnogram-api-record-publish` | Move a record `REVIEW` → `PUBLIC` by re-POSTing it with the new `CNA_private.state`. |
| `vulnogram-api-record-fetch` | Read a record back — full stored JSON, or just `CNA_private.state` (`--state-only`), or just the reviewer-comment array (`--comments-only`). Read-only; never POSTs. |
Expand Down Expand Up @@ -225,10 +225,11 @@ When the token has expired, the next API call returns `SessionExpired`:

```text
✗ Vulnogram redirected to the login page — the Bearer token is expired or invalid.
Log in again in the browser and re-run `vulnogram-api-setup` with the new token from /users/token.
Re-run `vulnogram-api-setup --pmc <project>` (recommended) or log in again in the browser and re-run `vulnogram-api-setup` with the new token from /users/token.
```

Re-running setup is a 30-second operation: open `/users/token`, log in again if prompted, copy the new token, paste it back.
Re-running setup is a 30-second operation: with `--pmc <project>`, approve the new token in the browser;
without it, open `/users/token`, log in again if prompted, copy the new token, paste it back.
The agentic skills detect `expired` automatically via `vulnogram-api-check`
and surface the re-setup hint in the same proposal
they would otherwise propose the API path in.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@

_EXPIRED_HINT = (
"Vulnogram redirected to the login page — the Bearer token is expired or invalid. "
"Log in again in the browser and re-run `vulnogram-api-setup` with the new token from /users/token."
"Re-run `vulnogram-api-setup --pmc <project>` (recommended) or log in again in the browser "
"and re-run `vulnogram-api-setup` with the new token from /users/token."
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,12 @@ def resolve_from_address(
)

for _attempt in range(3):
answer = prompter("ASF account: ").strip()
try:
answer = prompter("ASF account: ").strip()
except EOFError:
raise SystemExit(
"\n✗ No terminal for the ASF account prompt — re-run with `--from-address <id>@apache.org`."
) from None
if not answer:
print(" Empty — required for *.apache.org hosts. Try again.")
continue
Expand Down Expand Up @@ -364,7 +369,17 @@ def _setup_one(args: argparse.Namespace) -> int:
_print_walkthrough(args.host, from_address)
token = args.token
if not token:
token = getpass.getpass("Paste token: ").strip()
try:
token = getpass.getpass("Paste token: ").strip()
except EOFError:
# With --pmc we only get here when the server has no browser
# approval, so pointing back at --pmc would loop.
hint = (
"pass `--token <token>`"
if args.pmc
else "re-run with `--pmc <project>` (or pass `--token <token>`)"
)
raise SystemExit(f"\n✗ No terminal for the paste prompt — {hint}.") from None
if not token:
raise SystemExit("Empty token — aborting; nothing written.")

Expand Down Expand Up @@ -414,8 +429,9 @@ def _setup_one(args: argparse.Namespace) -> int:
print(
"✗ Vulnogram redirected to the login page. The token was either "
"copied incompletely or has already expired (it lives only as long "
"as the browser login that minted it). Log in again, open "
"/users/token for a new token, and re-run setup.",
"as the browser login that minted it). Re-run with "
"`vulnogram-api-setup --pmc <project>` (recommended) or log in again, "
"open /users/token for a new token, and re-run setup.",
file=sys.stderr,
)
return 2
Expand Down
59 changes: 59 additions & 0 deletions tools/cve-tool-vulnogram/oauth-api/tests/test_setup_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,3 +447,62 @@ def test_auto_scope_without_the_browser_flow_pastes_one_token(tmp_path, monkeypa
def test_auto_scope_rejects_a_single_out_path(tmp_path):
with pytest.raises(SystemExit, match="--scope"):
setup_session.main(["--pmc", "airflow", "--out", str(tmp_path / "s.json")])


def test_paste_prompt_eof_after_pmc_fallback_does_not_suggest_pmc(tmp_path, monkeypatch):
"""With --pmc on a server without browser approval, the paste prompt is
the fallback; its no-terminal hint must not send the operator back to --pmc."""

def fake_getpass(*_):
raise EOFError("simulated EOF")

monkeypatch.setattr(setup_session, "probe_server", lambda host: "unsupported")
monkeypatch.setattr(setup_session.getpass, "getpass", fake_getpass)
out = tmp_path / "session.json"
with pytest.raises(SystemExit) as excinfo:
setup_session.main(
["--pmc", "airflow", "--scope", "write", "--out", str(out), "--from-address", "you@apache.org"]
)
msg = str(excinfo.value)
assert "No terminal for the paste prompt" in msg
assert "--token <token>" in msg
assert "--pmc" not in msg
assert not out.exists()


def test_paste_prompt_eof_aborts_cleanly(tmp_path, monkeypatch):
"""When getpass encounters EOF, the script must abort cleanly
with a hint to re-run with --pmc instead of an unhandled traceback."""

def fake_getpass(*_):
raise EOFError("simulated EOF")

monkeypatch.setattr(setup_session.getpass, "getpass", fake_getpass)
out = tmp_path / "session.json"
with pytest.raises(SystemExit) as excinfo:
setup_session.main(
[
"--out",
str(out),
"--from-address",
"you@apache.org",
"--skip-validate",
]
)
msg = str(excinfo.value)
assert "No terminal for the paste prompt" in msg
assert "--pmc <project>" in msg
assert not out.exists()


def test_resolve_from_address_eof_aborts_cleanly():
"""When prompter encounters EOF, resolve_from_address must abort cleanly."""

def fake_prompter(_):
raise EOFError("simulated EOF")

with pytest.raises(SystemExit) as excinfo:
setup_session.resolve_from_address("cveprocess.apache.org", None, prompter=fake_prompter)
msg = str(excinfo.value)
assert "No terminal for the ASF account prompt" in msg
assert "--from-address" in msg
4 changes: 2 additions & 2 deletions tools/cve-tool-vulnogram/record.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ outcomes drive the proposal:
- **`valid` (exit 0)** — propose the API one-liner; the copy-paste
recipe stays available as a one-line *"or open `#source` and
paste"* fallback for users who want the visual confirmation.
- **`expired` (exit 1)** — propose `vulnogram-api-setup` first and
- **`expired` (exit 1)** — propose `vulnogram-api-setup --pmc <project>` first and
the API one-liner second, with the copy-paste recipe as the
fallback if the operator wants to skip re-setup for this CVE.
- **`not-configured` (exit 2)** — propose `vulnogram-api-setup`
- **`not-configured` (exit 2)** — propose `vulnogram-api-setup --pmc <project>`
with a one-line *"or skip setup and use the copy-paste flow
below"* hint. **Do not** force setup on an unwilling operator.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def out(action: str, why: str, **extra: Any) -> dict[str, Any]:
)
if probe in ("expired", "not-configured"):
why = (
"the session expired: remind the operator to re-run vulnogram-api-setup; manual-paste hand-off this run"
"the session expired: remind the operator to re-run vulnogram-api-setup --pmc <project>; manual-paste hand-off this run"
if probe == "expired"
else "no session is configured: manual-paste hand-off through the source tab"
)
Expand Down
Loading