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
16 changes: 14 additions & 2 deletions commands/paymob-check-hmac.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
description: Audit a Paymob webhook HMAC verification implementation for correctness and safe failure behavior.
argument-hint: "[path to the webhook handler, optional]"
disable-model-invocation: true
allowed-tools: Read Glob Grep
---

Audit the user's Paymob webhook HMAC verification code: $ARGUMENTS
Expand All @@ -10,14 +11,25 @@ Audit the user's Paymob webhook HMAC verification code: $ARGUMENTS
- Never ask the user to paste their HMAC secret, API key, or Secret Key into this conversation.
- Never read a `.env` file's actual secret value, and never echo, print, or write any secret into chat output, logs, or a file you create.
- The default mode is a **static code audit** — no computation is required to complete it.
- **Never audit the field order, digest, or callback shape from memory.** This command deliberately carries no copy of the spec. If you cannot read `hmac-verification.md`, you cannot do this audit — say so and stop. An audit performed from recall can pass a wrong field order as correct, which is the exact failure this command exists to catch: HMAC verification that looks verified and silently is not.

## Step 0 — locate the spec

`hmac-verification.md` lives in the skill's `references/` directory. Use the first of these that exists:

1. `${CLAUDE_PLUGIN_ROOT}/skills/paymob-integration/references/` — plugin install
2. `~/.claude/skills/paymob-integration/references/` — personal skill install. `~` is the user's home directory; on Windows expand it yourself (`%USERPROFILE%\.claude\skills\…`), since a literal `~` will not resolve there.
3. `skills/paymob-integration/references/` — repository checkout, relative to the working directory. This only resolves if the user happens to be working inside the skill repo, so treat path 2 as the reliable one for a personal install.

`${CLAUDE_PLUGIN_ROOT}` is substituted only for plugin installs. If you see it unexpanded, you are not in one — use path 2 or 3. If none resolve, search for `hmac-verification.md` under any `paymob-integration` directory. Confirm you have actually read the file before starting Step 2.

## Step 1 — locate the code

If `$ARGUMENTS` names a path, start there. Otherwise search for the callback route bound to `notification_url` / the `hmac` query parameter, or for `PAYMOB_HMAC_SECRET` usage.

## Step 2 — audit against `${CLAUDE_PLUGIN_ROOT}/skills/paymob-integration/references/hmac-verification.md`
## Step 2 — audit against `references/hmac-verification.md`

Read that file first — don't rely on memory for the field order or algorithm, since this command carries no copy of the spec. Then check each item below and report pass/fail with file/line references:
Read that file first, from the root you resolved in Step 0 — don't rely on memory for the field order or algorithm. Then check each item below and report pass/fail with file/line references:

1. **Digest**: HMAC-**SHA-512**, not SHA-256 or any other digest.
2. **Source and order**: the concatenation reads fields from `body.obj` (the POST payload) in exactly the field order given in `hmac-verification.md` — flag any other ordering, including "alphabetical by key name," as a failure.
Expand Down
14 changes: 12 additions & 2 deletions commands/paymob-explain-error.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
---
description: Explain a Paymob error code, HTTP status, or error message and give the cause and fix.
argument-hint: "[error code, HTTP status, or error message]"
allowed-tools: Read
allowed-tools: Read Glob
---

The user wants this Paymob error explained: $ARGUMENTS

1. Read the **Troubleshooting** table in `${CLAUDE_PLUGIN_ROOT}/skills/paymob-integration/SKILL.md` and match `$ARGUMENTS` against its rows (by HTTP status, error text, or symptom). Quote the cause(s) listed for the matching row — do not paraphrase from memory, read the table each time in case it has changed.
**Locating the skill files.** `SKILL.md` and its `references/` directory live together. Use the first of these that exists:

1. `${CLAUDE_PLUGIN_ROOT}/skills/paymob-integration/` — plugin install
2. `~/.claude/skills/paymob-integration/` — personal skill install. `~` is the user's home directory; on Windows expand it yourself (`%USERPROFILE%\.claude\skills\…`), since a literal `~` will not resolve there.
3. `skills/paymob-integration/` — repository checkout, relative to the working directory

`${CLAUDE_PLUGIN_ROOT}` is substituted only for plugin installs. If you see it unexpanded, you are not in one — use path 2 or 3. If none resolve, search for `SKILL.md` under any `paymob-integration` directory. Paths written as `references/…` below are relative to whichever root you resolved.

**If you cannot read the files, say so and stop.** Do not explain a Paymob error from memory — a plausible-sounding wrong cause sends the merchant to debug the wrong layer.

1. Read the **Troubleshooting** table in `SKILL.md` and match `$ARGUMENTS` against its rows (by HTTP status, error text, or symptom). Quote the cause(s) listed for the matching row — do not paraphrase from memory, read the table each time in case it has changed.
2. If the fix involves code, open the reference file matching the user's project language/framework — `references/code-nodejs.md`, `references/code-python.md`, `references/code-php.md`, `references/code-dotnet.md`, `references/code-ruby.md`, or `references/code-frontend.md` — and adapt its corrected snippet to the user's project. Do not write a fix that contradicts what's in that file (for example, don't invent a different HMAC digest, header scheme, or amount unit than what the reference shows).
3. If the matching row is the HMAC-mismatch row, also read `references/hmac-verification.md` before answering and get the field order, digest algorithm, and the `obj.id`/`order.id` (POST) vs `id`/`order_id` (GET redirect) distinction from that file directly — never restate the field order from memory or from this command file, since this file intentionally carries no copy of it.
4. If `$ARGUMENTS` does not match any row in the table, say so explicitly rather than guessing at a cause. Then:
Expand Down
14 changes: 12 additions & 2 deletions commands/paymob-test-cards.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
---
description: Show Paymob sandbox test card and wallet credentials, optionally filtered by payment method.
argument-hint: "[card|wallet|kiosk|bnpl]"
allowed-tools: Read
allowed-tools: Read Glob
---

Read `${CLAUDE_PLUGIN_ROOT}/skills/paymob-integration/references/test-credentials.md` in full and present the sandbox test credentials it contains to the user. Do not use any test numbers from memory — this file is the only source.
Read `references/test-credentials.md` in full and present the sandbox test credentials it contains to the user. Do not use any test numbers from memory — this file is the only source.

**Locating the reference file.** Use the first of these that exists:

1. `${CLAUDE_PLUGIN_ROOT}/skills/paymob-integration/references/` — plugin install
2. `~/.claude/skills/paymob-integration/references/` — personal skill install. `~` is the user's home directory; on Windows expand it yourself (`%USERPROFILE%\.claude\skills\…`), since a literal `~` will not resolve there.
3. `skills/paymob-integration/references/` — repository checkout, relative to the working directory

`${CLAUDE_PLUGIN_ROOT}` is substituted only for plugin installs. If you see it unexpanded, you are not in one — use path 2 or 3. If none resolve, search for `test-credentials.md` under any `paymob-integration` directory.

**If you cannot read the file, say so and stop.** Do not list card numbers from memory — stale or invented test credentials waste a merchant's debugging time on a payment that was never going to succeed.

Argument handling for `$ARGUMENTS`:
- `card` → show only the Mastercard and Visa test card sections.
Expand Down
30 changes: 30 additions & 0 deletions scripts/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@
HMAC_FIELD_ORDER_RESTATEMENT_PATTERN = re.compile(
r"amount_cents\s*\r?\n\s*created_at\s*\r?\n\s*currency"
)
# ${CLAUDE_PLUGIN_ROOT} is substituted only for plugin installs. A command that
# names it as the sole location breaks when the skill is installed as a personal
# skill under ~/.claude/skills/, which is what agents do when asked to install
# the skill from GitHub. Every command must offer a non-plugin fallback.
COMMAND_FALLBACK_PATH_PATTERNS = (
re.compile(r"~/\.claude/skills/paymob-integration"),
re.compile(r"(?<!/)\bskills/paymob-integration"),
)
# A command that cannot read its reference file must stop rather than answer
# from the model's own recall; for the HMAC audit that failure mode is a wrong
# field order presented as verified.
COMMAND_FAIL_CLOSED_PATTERN = re.compile(
r"(cannot read|can't read).{0,80}(say so and stop|stop)"
r"|(say so and stop).{0,120}(from memory|from recall)",
re.IGNORECASE | re.DOTALL,
)


def load_json(path: Path, errors: list[str]) -> dict:
Expand Down Expand Up @@ -488,6 +504,20 @@ def validate_commands(errors: list[str]) -> None:
if COMMAND_SECRET_PATTERN.search(body):
errors.append(f"{label} appears to contain a hardcoded secret or key")

if "${CLAUDE_PLUGIN_ROOT}" in body and not any(
pattern.search(body) for pattern in COMMAND_FALLBACK_PATH_PATTERNS
):
errors.append(
f"{label} names ${{CLAUDE_PLUGIN_ROOT}} without a non-plugin fallback "
"path; the reference files are unreachable in a personal-skill install"
)

if not COMMAND_FAIL_CLOSED_PATTERN.search(body):
errors.append(
f"{label} must tell the agent to stop rather than answer from memory "
"when it cannot read its reference file"
)


def validate_links(errors: list[str]) -> None:
for markdown in ROOT.rglob("*.md"):
Expand Down
Loading