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
119 changes: 119 additions & 0 deletions tests/samourai-crew/audit/audit_cross_realm_p_arithmetic.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/bin/sh
# Targets: fix(gnovm): cross-realm /p/-type arithmetic
# Commit: 9e56b0c77
# Vector: arithmetic on a /p/-defined type passed across realm boundaries could
# produce incorrect results. A Counter defined in /p/ is incremented by realm A,
# passed (via a run script) to realm B which increments it again, then read back
# from realm A. Result must equal 2.

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=common.sh
. "$SCRIPT_DIR/common.sh"

SUFFIX=$(date +%s)
PKGPATH_P="gno.land/p/${KEY_ADDR}/audit/pcounter${SUFFIX}"
PKGPATH_R="gno.land/r/${KEY_ADDR}/audit/arithmetic${SUFFIX}"
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

echo "9e56b0c77 — Cross-realm /p/-type arithmetic"
echo " Counter pkg : $PKGPATH_P"
echo " Realm : $PKGPATH_R"

mkdir -p "$TMPDIR/p" "$TMPDIR/r"

# --- deploy /p/ Counter type ---
cat > "$TMPDIR/p/pcounter.gno" << EOF
package pcounter

type Counter struct{ Value int }

func Inc(c *Counter) { c.Value++ }
EOF

cat > "$TMPDIR/p/gnomod.toml" << EOF
module = "${PKGPATH_P}"
gno = "0.9"
EOF

echo -n " Deploying /p/ Counter... "
DEPLOY_P=$(echo "$PASSWORD" | gnokey maketx addpkg \
-pkgpath "$PKGPATH_P" -pkgdir "$TMPDIR/p" \
-gas-fee 1000000ugnot -gas-wanted 10000000 \
-broadcast -chainid "$CHAINID" -remote "$RPC" \
-insecure-password-stdin \
-home "$GNOKEY_HOME" \
"$KEY" 2>&1)
if echo "$DEPLOY_P" | grep -q "OK!"; then echo "OK"; else
echo "FAILED"; echo "$DEPLOY_P"; exit 1
fi

# --- deploy /r/ realm that holds a Counter ---
cat > "$TMPDIR/r/arithmetic.gno" << EOF
package arithmetic

import counter "${PKGPATH_P}"

var c = &counter.Counter{Value: 0}

func Inc() {
counter.Inc(c)
}

func GetValue() int { return c.Value }
EOF

cat > "$TMPDIR/r/gnomod.toml" << EOF
module = "${PKGPATH_R}"
gno = "0.9"
EOF

echo -n " Deploying realm... "
DEPLOY_R=$(echo "$PASSWORD" | gnokey maketx addpkg \
-pkgpath "$PKGPATH_R" -pkgdir "$TMPDIR/r" \
-gas-fee 1000000ugnot -gas-wanted 10000000 \
-broadcast -chainid "$CHAINID" -remote "$RPC" \
-insecure-password-stdin \
-home "$GNOKEY_HOME" \
"$KEY" 2>&1)
if echo "$DEPLOY_R" | grep -q "OK!"; then echo "OK"; else
echo "FAILED"; echo "$DEPLOY_R"; exit 1
fi

# --- increment twice via maketx run ---
cat > "$TMPDIR/inc.gno" << EOF
package main

import r "${PKGPATH_R}"

func main() {
r.Inc()
r.Inc()
}
EOF

echo -n " Incrementing counter twice... "
INC=$(echo "$PASSWORD" | gnokey maketx run \
-gas-fee 1000000ugnot -gas-wanted 5000000 \
-broadcast -chainid "$CHAINID" -remote "$RPC" \
-insecure-password-stdin \
-home "$GNOKEY_HOME" \
"$KEY" "$TMPDIR/inc.gno" 2>&1)
if echo "$INC" | grep -q "OK!"; then echo "OK"; else
echo "FAILED"; echo "$INC"; exit 1
fi

# --- verify result ---
echo -n " Querying counter value (expect 2)... "
RESULT=$(gnokey query "vm/qeval" \
-data "${PKGPATH_R}.GetValue()" \
-remote "$RPC" 2>&1)

if echo "$RESULT" | grep -qE '\(2 int\)|"2"'; then
echo "✅ PATCHED — cross-realm /p/ arithmetic correct (value=2)"
elif echo "$RESULT" | grep -qE '\(0 int\)|"0"'; then
echo "❌ VULNERABLE — counter still 0 after two increments (cross-realm arithmetic broken)"
exit 1
else
echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1
fi
79 changes: 79 additions & 0 deletions tests/samourai-crew/audit/audit_map_key_gas.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/bin/sh
# Targets: fix: consume gas on ComputeMapKey
# Commit: 720af8bcd
# Vector: map key computation (insertions, lookups with complex struct keys)
# did not consume gas proportionally. A map with deeply nested struct keys
# allowed a DoS with unbounded CPU at constant gas cost.
# Test 1: insert 10_000 complex-key entries with low gas → must OOG (PATCHED).
# Test 2: insert 10 simple-key entries with ample gas → must succeed.

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=common.sh
. "$SCRIPT_DIR/common.sh"

TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

echo "720af8bcd — Gas consumption on ComputeMapKey"

# --- test 1: many complex struct keys, low gas → expect OOG ---
cat > "$TMPDIR/mapbig.gno" << EOF
package main

type Key struct {
A, B, C, D, E string
}

func main() {
m := make(map[Key]int)
for i := 0; i < 10000; i++ {
m[Key{"aaa", "bbb", "ccc", "ddd", "eee"}] = i
}
}
EOF

echo -n " Test 1: 10_000 complex map keys, gas=100_000 (expect OOG)... "
BIG=$(echo "$PASSWORD" | gnokey maketx run \
-gas-fee 1000000ugnot -gas-wanted 100000 \
-broadcast -chainid "$CHAINID" -remote "$RPC" \
-insecure-password-stdin \
-home "$GNOKEY_HOME" \
"$KEY" "$TMPDIR/mapbig.gno" 2>&1)

if echo "$BIG" | grep -qiE "out of gas|OOG|gas"; then
echo "OOG (expected)"
elif echo "$BIG" | grep -q "OK!"; then
echo "❌ VULNERABLE — 10_000 complex map insertions succeeded with only 100_000 gas"
exit 1
else
# Any rejection is acceptable — might be a gas format error or similar
echo "rejected ($(echo "$BIG" | head -1))"
fi

# --- test 2: 10 simple keys, ample gas → expect success ---
cat > "$TMPDIR/mapsmall.gno" << EOF
package main

func main() {
m := make(map[string]int)
for i := 0; i < 10; i++ {
m["key"] = i
}
}
EOF

echo -n " Test 2: 10 simple map keys, gas=1_000_000 (expect OK)... "
SMALL=$(echo "$PASSWORD" | gnokey maketx run \
-gas-fee 1000000ugnot -gas-wanted 1000000 \
-broadcast -chainid "$CHAINID" -remote "$RPC" \
-insecure-password-stdin \
-home "$GNOKEY_HOME" \
"$KEY" "$TMPDIR/mapsmall.gno" 2>&1)

if echo "$SMALL" | grep -q "OK!"; then
echo "✅ PATCHED — complex map keys metered (OOG on large), simple map works normally"
else
echo "FAILED (simple map rejected unexpectedly)"
echo "$SMALL"
exit 1
fi
80 changes: 80 additions & 0 deletions tests/samourai-crew/audit/audit_nil_func_call.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/bin/sh
# Targets: fix(gnovm): generate proper Gno panic for nil function calls
# Commit: a7e4c34b0
# Vector: calling a nil function variable (var f func(); f()) caused a node crash
# (Go-level panic escaping the VM) instead of a proper Gno panic. After the fix,
# the VM catches the nil call and returns a transaction-level Gno panic, leaving
# the node intact.

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=common.sh
. "$SCRIPT_DIR/common.sh"

SUFFIX=$(date +%s)
PKGPATH="gno.land/r/${KEY_ADDR}/audit/nilfunc${SUFFIX}"
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

echo "a7e4c34b0 — Nil function call → proper Gno panic (not node crash)"
echo " Package: $PKGPATH"

# --- deploy realm with a nil function variable ---
cat > "$TMPDIR/nilfunc.gno" << EOF
package nilfunc

var f func()

func CallNil() {
f()
}
EOF

cat > "$TMPDIR/gnomod.toml" << EOF
module = "${PKGPATH}"
gno = "0.9"
EOF

echo -n " Deploying realm... "
DEPLOY=$(echo "$PASSWORD" | gnokey maketx addpkg \
-pkgpath "$PKGPATH" -pkgdir "$TMPDIR" \
-gas-fee 1000000ugnot -gas-wanted 10000000 \
-broadcast -chainid "$CHAINID" -remote "$RPC" \
-insecure-password-stdin \
-home "$GNOKEY_HOME" \
"$KEY" 2>&1)
if echo "$DEPLOY" | grep -q "OK!"; then echo "OK"; else
echo "FAILED"; echo "$DEPLOY"; exit 1
fi

# --- call the nil function ---
cat > "$TMPDIR/callnil.gno" << EOF
package main

import v "${PKGPATH}"

func main() { v.CallNil() }
EOF

echo -n " Calling nil function... "
CALL=$(echo "$PASSWORD" | gnokey maketx run \
-gas-fee 1000000ugnot -gas-wanted 5000000 \
-broadcast -chainid "$CHAINID" -remote "$RPC" \
-insecure-password-stdin \
-home "$GNOKEY_HOME" \
"$KEY" "$TMPDIR/callnil.gno" 2>&1)
echo "$(echo "$CALL" | grep -oE 'OK!|error|panic|nil' | head -1)"

# TX must be rejected (nil call should panic)
if echo "$CALL" | grep -q "OK!"; then
echo "❌ VULNERABLE — nil function call succeeded (unexpected)"
exit 1
fi

# --- verify node is still alive ---
echo -n " Checking node liveness... "
if gnokey query "bank/balances/${KEY_ADDR}" -remote "$RPC" > /dev/null 2>&1; then
echo "✅ PATCHED — nil function call rejected gracefully, node still responsive"
else
echo "❌ VULNERABLE — node unresponsive after nil function call (crash)"
exit 1
fi
85 changes: 85 additions & 0 deletions tests/samourai-crew/audit/audit_panic_log_dos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/bin/sh
# Targets: fix(vm): bound panic-Log rendering to prevent unmetered long running txs
# Commit: 4bb497abe
# Vector: a panic with a very long message triggered unmetered rendering in the VM,
# allowing a DoS via transactions that would block the node regardless of gas.
# After the fix, panic message rendering is bounded by gas.
# Test: send a tx that panics with a large message using a bounded timeout;
# if the node hangs past 30s the rendering is unmetered.

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=common.sh
. "$SCRIPT_DIR/common.sh"

SUFFIX=$(date +%s)
PKGPATH="gno.land/r/${KEY_ADDR}/audit/panicdos${SUFFIX}"
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

echo "4bb497abe — Panic-Log DoS (unmetered panic message rendering)"
echo " Package: $PKGPATH"

# --- deploy realm with a large-panic function ---
cat > "$TMPDIR/panicdos.gno" << EOF
package panicdos

import "strings"

func Boom() {
panic(strings.Repeat("A", 100_000))
}
EOF

cat > "$TMPDIR/gnomod.toml" << EOF
module = "${PKGPATH}"
gno = "0.9"
EOF

echo -n " Deploying realm... "
DEPLOY=$(echo "$PASSWORD" | gnokey maketx addpkg \
-pkgpath "$PKGPATH" -pkgdir "$TMPDIR" \
-gas-fee 1000000ugnot -gas-wanted 10000000 \
-broadcast -chainid "$CHAINID" -remote "$RPC" \
-insecure-password-stdin \
-home "$GNOKEY_HOME" \
"$KEY" 2>&1)
if echo "$DEPLOY" | grep -q "OK!"; then echo "OK"; else
echo "FAILED"; echo "$DEPLOY"; exit 1
fi

# --- call Boom() with a 30s timeout ---
# PATCHED: call returns quickly (OOG or panic error) within the timeout.
# VULNERABLE: call hangs past 30s (unmetered rendering blocks the node).
cat > "$TMPDIR/boom.gno" << EOF
package main

import v "${PKGPATH}"

func main() { v.Boom() }
EOF

echo -n " Calling Boom() with 30s timeout... "
CALL=$(timeout 30 sh -c "echo '$PASSWORD' | gnokey maketx run \
-gas-fee 1000000ugnot -gas-wanted 2000000 \
-broadcast -chainid \"$CHAINID\" -remote \"$RPC\" \
-insecure-password-stdin \
-home \"$GNOKEY_HOME\" \
\"$KEY\" \"$TMPDIR/boom.gno\"" 2>&1)
TIMEOUT_STATUS=$?

if [ "$TIMEOUT_STATUS" -eq 124 ]; then
echo "❌ VULNERABLE — call hung past 30s (unmetered panic rendering)"
exit 1
fi

# Call returned within timeout — should be a tx rejection (panic or OOG)
echo "returned ($(echo "$CALL" | grep -oE 'OK!|error|panic' | head -1))"

# Verify node is still responsive
echo -n " Checking node liveness... "
if gnokey query "bank/balances/${KEY_ADDR}" -remote "$RPC" > /dev/null 2>&1; then
echo "✅ PATCHED — Boom() returned within 30s and node is still responsive"
else
echo "❌ Node unresponsive after panic tx"
exit 1
fi
Loading
Loading