archy/scripts/test-nip07.sh
Dorian 540836f3d6 feat: fix NIP-07 signing to use node Nostr key, add test script
Added node.nostr-sign RPC that uses the node-level Nostr key (matching
getPublicKey), fixing pubkey mismatch where identity.nostr-sign used a
different key. Updated appLauncher to call node.nostr-sign. Added
nostr_sign_hash() to nostr_discovery.rs. Created test-nip07.sh with
11 automated checks (INSTALL-02).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 03:18:45 +00:00

125 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# test-nip07.sh — Validate NIP-07 Nostr signing infrastructure
#
# Tests server-side components of NIP-07 signing.
# Browser-based tests (window.nostr in DevTools) must be done manually.
#
# Usage: ./scripts/test-nip07.sh [target-ip]
set -uo pipefail
TARGET="${1:-192.168.1.228}"
SSH_KEY="${ARCHIPELAGO_SSH_KEY:-$HOME/.ssh/archipelago-deploy}"
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o ConnectTimeout=10 archipelago@$TARGET"
PASS=0
FAIL=0
check() {
local name="$1"
local ok="$2"
if [ "$ok" = "true" ]; then
echo "$name"
((PASS++))
else
echo "$name"
((FAIL++))
fi
}
# Extract JSON field using python3 (runs locally)
json_get() {
python3 -c "import sys,json; d=json.load(sys.stdin); r=d.get('result',{}); print(r.get('$1','') if isinstance(r,dict) else '')" 2>/dev/null
}
echo "🔑 NIP-07 Nostr Signing Test — $TARGET"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Login and get CSRF token
echo ""
echo "Authenticating..."
$SSH "curl -s -c /tmp/cookiejar http://localhost:5678/rpc/v1 -H 'Content-Type: application/json' -d '{\"method\":\"auth.login\",\"params\":{\"password\":\"password123\"}}'" >/dev/null 2>&1
CSRF=$($SSH "grep csrf_token /tmp/cookiejar 2>/dev/null | awk '{print \$NF}'" 2>/dev/null)
echo " CSRF: ${CSRF:0:16}..."
rpc() {
local method="$1"
local params="${2:-}"
local body
if [ -n "$params" ]; then
body="{\"method\":\"$method\",\"params\":$params}"
else
body="{\"method\":\"$method\"}"
fi
$SSH "curl -s -b /tmp/cookiejar -H 'Content-Type: application/json' -H 'X-CSRF-Token: $CSRF' http://localhost:5678/rpc/v1 -d '$body'" 2>/dev/null
}
# 1. nostr-provider.js exists on server
echo ""
echo "1. nostr-provider.js served by nginx"
JS_EXISTS=$($SSH "curl -s -o /dev/null -w '%{http_code}' http://localhost/nostr-provider.js" 2>/dev/null)
check "nostr-provider.js returns 200" "$([ "$JS_EXISTS" = "200" ] && echo true || echo false)"
# 2. nostr-provider.js injected into iframe app
echo ""
echo "2. Script injection via sub_filter"
INJECT_COUNT=$($SSH "curl -s http://localhost/app/mempool/ 2>/dev/null | grep -c 'nostr-provider.js'" 2>/dev/null)
check "Injected into /app/mempool/" "$([ "$INJECT_COUNT" -ge 1 ] && echo true || echo false)"
# 3. node.nostr-pubkey returns valid pubkey
echo ""
echo "3. Node Nostr pubkey"
PUBKEY_RESP=$(rpc "node.nostr-pubkey")
NODE_PK=$(echo "$PUBKEY_RESP" | json_get "nostr_pubkey")
check "node.nostr-pubkey returns hex pubkey (${#NODE_PK} chars)" "$([ ${#NODE_PK} -eq 64 ] && echo true || echo false)"
NODE_NPUB=$(echo "$PUBKEY_RESP" | json_get "nostr_npub")
check "npub format valid" "$(echo "$NODE_NPUB" | grep -q '^npub1' && echo true || echo false)"
# 4. node.nostr-sign returns signed event with matching pubkey
echo ""
echo "4. Event signing"
CREATED_AT=$(date +%s)
SIGN_RESP=$(rpc "node.nostr-sign" "{\"event\":{\"kind\":1,\"content\":\"NIP-07 automated test\",\"created_at\":$CREATED_AT,\"tags\":[]}}")
SIGN_PK=$(echo "$SIGN_RESP" | json_get "pubkey")
SIGN_SIG=$(echo "$SIGN_RESP" | json_get "sig")
SIGN_ID=$(echo "$SIGN_RESP" | json_get "id")
check "Signed event has pubkey (${#SIGN_PK} chars)" "$([ ${#SIGN_PK} -eq 64 ] && echo true || echo false)"
check "Signed event has signature (${#SIGN_SIG} chars)" "$([ ${#SIGN_SIG} -gt 60 ] && echo true || echo false)"
check "Signed event has id hash (${#SIGN_ID} chars)" "$([ ${#SIGN_ID} -eq 64 ] && echo true || echo false)"
check "Signing pubkey matches node pubkey" "$([ "$SIGN_PK" = "$NODE_PK" ] && echo true || echo false)"
# 5. Signed event content matches input
echo ""
echo "5. Event content integrity"
SIGN_CONTENT=$(echo "$SIGN_RESP" | json_get "content")
SIGN_KIND=$(echo "$SIGN_RESP" | json_get "kind")
check "Content preserved" "$([ "$SIGN_CONTENT" = "NIP-07 automated test" ] && echo true || echo false)"
check "Kind preserved" "$([ "$SIGN_KIND" = "1" ] && echo true || echo false)"
# 6. NIP-04/NIP-44 encrypt/decrypt endpoints exist
echo ""
echo "6. NIP-04 encrypt/decrypt"
ENC_RESP=$(rpc "identity.nostr-encrypt-nip04" "{\"pubkey\":\"$NODE_PK\",\"plaintext\":\"hello\"}")
ENC_ERR=$(echo "$ENC_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); e=d.get('error'); print(e.get('message','') if e else 'none')" 2>/dev/null)
check "NIP-04 encrypt endpoint exists" "$(echo "$ENC_ERR" | grep -qv 'Unknown method' && echo true || echo false)"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Results: $PASS passed, $FAIL failed"
echo ""
echo "📝 Manual browser test steps:"
echo " 1. Open http://$TARGET/dashboard/apps"
echo " 2. Launch an iframe app (e.g., Mempool)"
echo " 3. Open DevTools console (F12)"
echo " 4. Run: window.nostr"
echo " → Should return object with getPublicKey, signEvent"
echo " 5. Run: await window.nostr.getPublicKey()"
echo " → Should return: $NODE_PK"
echo " 6. Run: await window.nostr.signEvent({kind:1,content:'test',created_at:Math.floor(Date.now()/1000),tags:[]})"
echo " → Consent modal should appear in parent frame"
echo " → After approval, should return signed event with sig field"
[ $FAIL -eq 0 ] && exit 0 || exit 1