Files
explorer-monorepo/scripts/check-failed-transaction-details.sh
Devin 4cbea21d8f fix(security): fail-fast on missing JWT_SECRET, harden CSP, strip hardcoded passwords
backend/api/rest/server.go:
- NewServer() now delegates to loadJWTSecret(), which:
    - Rejects JWT_SECRET < 32 bytes (log.Fatal).
    - Requires JWT_SECRET when APP_ENV=production or GO_ENV=production.
    - Generates a 32-byte crypto/rand ephemeral secret in dev only.
    - Treats rand.Read failure as fatal (removes the prior time-based
      fallback that was deterministic and forgeable).
- Default Content-Security-Policy rewritten:
    - Drops 'unsafe-inline' and 'unsafe-eval'.
    - Drops private CIDRs (192.168.11.221:854[5|6]).
    - Adds frame-ancestors 'none', base-uri 'self', form-action 'self'.
    - CSP_HEADER is required in production; fatal if unset there.

backend/api/rest/server_security_test.go (new):
- Covers the three loadJWTSecret() paths (valid, whitespace-trimmed,
  ephemeral in dev).
- Covers isProductionEnv() across APP_ENV / GO_ENV combinations.
- Asserts defaultDevCSP contains no unsafe directives or private CIDRs
  and includes the frame-ancestors / base-uri / form-action directives.

scripts/*.sh:
- Removed 'L@kers2010' default value from SSH_PASSWORD / NEW_PASSWORD in
  7 helper scripts. Each script now fails with exit 2 and points to
  docs/SECURITY.md if the password isn't supplied via env or argv.

EXECUTE_DEPLOYMENT.sh, EXECUTE_NOW.sh:
- Replaced hardcoded DB_PASSWORD='L@ker$2010' with a ':?' guard that
  aborts with a clear error if DB_PASSWORD (and, for EXECUTE_DEPLOYMENT,
  RPC_URL) is not exported. Other env vars keep sensible non-secret
  defaults via ${VAR:-default}.

README.md:
- Removed the hardcoded Database Password / RPC URL lines. Replaced with
  an env-variable reference table pointing at docs/SECURITY.md and
  docs/DATABASE_CONNECTION_GUIDE.md.

docs/DEPLOYMENT.md:
- Replaced 'PASSWORD: SSH password (default: L@kers2010)' with a
  required-no-default contract and a link to docs/SECURITY.md.

docs/SECURITY.md (new):
- Full secret inventory keyed to the env variable name and the file that
  consumes it.
- Five-step rotation checklist covering the Postgres role, the Proxmox
  VM SSH password, JWT_SECRET, vendor API keys, and a gitleaks-based
  history audit.
- Explicit note that merging secret-scrub PRs does NOT invalidate
  already-leaked credentials; rotation is the operator's responsibility.

Verification:
- go build ./... + go vet ./... pass clean.
- Targeted tests (LoadJWTSecret*, IsProduction*, DefaultDevCSP*) pass.

Advances completion criterion 2 (Secrets & config hardened). Residual
leakage from START_HERE.md / LETSENCRYPT_CONFIGURATION_GUIDE.md is
handled by PR #2 (doc consolidation), which deletes those files.
2026-04-18 19:02:27 +00:00

159 lines
7.3 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Check Failed Transaction Details on RPC Node
# Analyzes specific failed transactions
set -euo pipefail
RPC_IP="${1:-192.168.11.250}"
SSH_PASSWORD="${SSH_PASSWORD:-${2:-}}"
if [ -z "${SSH_PASSWORD}" ]; then
echo "ERROR: SSH_PASSWORD is required. Pass it as an argument or export SSH_PASSWORD in the environment." >&2
echo " Hardcoded default removed for security; see docs/SECURITY.md." >&2
exit 2
fi
TX_HASH="${3:-0x4dc9f5eedf580c2b37457916b04048481aba19cf3c1a106ea1ee9eefa0dc03c8}"
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║ CHECKING FAILED TRANSACTION DETAILS ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""
echo "Transaction: $TX_HASH"
echo "RPC: $RPC_IP"
echo ""
# Check if sshpass is available
if ! command -v sshpass >/dev/null 2>&1; then
echo "⚠️ sshpass not installed. Installing..."
sudo apt-get update -qq && sudo apt-get install -y sshpass 2>/dev/null || {
echo "❌ Cannot install sshpass automatically"
exit 1
}
fi
# Get transaction receipt
echo "═══════════════════════════════════════════════════════════════"
echo "1. TRANSACTION RECEIPT"
echo "═══════════════════════════════════════════════════════════════"
echo ""
RECEIPT=$(sshpass -p "$SSH_PASSWORD" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 \
root@"$RPC_IP" \
"curl -s -X POST -H 'Content-Type: application/json' \
--data '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionReceipt\",\"params\":[\"$TX_HASH\"],\"id\":1}' \
http://localhost:8545" 2>&1)
if [ -n "$RECEIPT" ]; then
echo "$RECEIPT" | jq '.' 2>/dev/null || echo "$RECEIPT"
STATUS=$(echo "$RECEIPT" | jq -r '.result.status // empty' 2>/dev/null || echo "")
GAS_USED=$(echo "$RECEIPT" | jq -r '.result.gasUsed // empty' 2>/dev/null || echo "")
CONTRACT=$(echo "$RECEIPT" | jq -r '.result.contractAddress // empty' 2>/dev/null || echo "")
echo ""
echo "Status: $STATUS"
echo "Gas Used: $GAS_USED"
echo "Contract Address: $CONTRACT"
else
echo "❌ Could not retrieve transaction receipt"
fi
echo ""
# Get transaction details
echo "═══════════════════════════════════════════════════════════════"
echo "2. TRANSACTION DETAILS"
echo "═══════════════════════════════════════════════════════════════"
echo ""
TX_DETAILS=$(sshpass -p "$SSH_PASSWORD" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 \
root@"$RPC_IP" \
"curl -s -X POST -H 'Content-Type: application/json' \
--data '{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionByHash\",\"params\":[\"$TX_HASH\"],\"id\":1}' \
http://localhost:8545" 2>&1)
if [ -n "$TX_DETAILS" ]; then
echo "$TX_DETAILS" | jq '.' 2>/dev/null || echo "$TX_DETAILS"
FROM=$(echo "$TX_DETAILS" | jq -r '.result.from // empty' 2>/dev/null || echo "")
TO=$(echo "$TX_DETAILS" | jq -r '.result.to // empty' 2>/dev/null || echo "")
GAS=$(echo "$TX_DETAILS" | jq -r '.result.gas // empty' 2>/dev/null || echo "")
GAS_PRICE=$(echo "$TX_DETAILS" | jq -r '.result.gasPrice // empty' 2>/dev/null || echo "")
INPUT=$(echo "$TX_DETAILS" | jq -r '.result.input // empty' 2>/dev/null || echo "")
INPUT_LEN=${#INPUT}
echo ""
echo "From: $FROM"
echo "To: $TO (null = contract creation)"
echo "Gas Limit: $GAS"
echo "Gas Price: $GAS_PRICE"
echo "Input Length: $INPUT_LEN chars (contract bytecode)"
else
echo "❌ Could not retrieve transaction details"
fi
echo ""
# Search logs for this transaction
echo "═══════════════════════════════════════════════════════════════"
echo "3. LOGS FOR THIS TRANSACTION"
echo "═══════════════════════════════════════════════════════════════"
echo ""
TX_SHORT="${TX_HASH:0:20}..."
LOG_ENTRIES=$(sshpass -p "$SSH_PASSWORD" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 \
root@"$RPC_IP" \
"journalctl -u besu-rpc -n 5000 --no-pager 2>/dev/null | grep -i '$TX_SHORT\|${TX_HASH:2}' || echo 'NO_LOGS'" 2>&1)
if echo "$LOG_ENTRIES" | grep -qv "NO_LOGS"; then
echo "$LOG_ENTRIES" | head -20
else
echo " No log entries found for this transaction"
echo " (Transaction may have been processed but not logged)"
fi
echo ""
# Try to get revert reason (if available)
echo "═══════════════════════════════════════════════════════════════"
echo "4. REVERT REASON (if available)"
echo "═══════════════════════════════════════════════════════════════"
echo ""
# Try debug_traceTransaction if available
TRACE=$(sshpass -p "$SSH_PASSWORD" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 \
root@"$RPC_IP" \
"curl -s -X POST -H 'Content-Type: application/json' \
--data '{\"jsonrpc\":\"2.0\",\"method\":\"debug_traceTransaction\",\"params\":[\"$TX_HASH\",{\"tracer\":\"callTracer\"}],\"id\":1}' \
http://localhost:8545" 2>&1)
if [ -n "$TRACE" ] && ! echo "$TRACE" | grep -q "method not found\|not available"; then
echo "$TRACE" | jq '.' 2>/dev/null | head -50 || echo "$TRACE" | head -50
else
echo " debug_traceTransaction not available or not enabled"
echo " (Requires DEBUG API to be enabled)"
fi
echo ""
# Summary
echo "═══════════════════════════════════════════════════════════════"
echo "SUMMARY"
echo "═══════════════════════════════════════════════════════════════"
echo ""
if [ "$STATUS" = "0x0" ]; then
echo "❌ Transaction FAILED (status 0x0)"
echo ""
echo "Possible causes:"
echo " 1. Contract constructor reverted"
echo " 2. Out of gas (but gas was provided)"
echo " 3. Network-level restriction"
echo " 4. Invalid bytecode"
echo " 5. Network state issue"
echo ""
echo "Next steps:"
echo " - Check if contract bytecode is valid"
echo " - Verify network allows contract creation"
echo " - Check validator logs for restrictions"
echo " - Try deploying from validator node"
fi
echo ""