diff --git a/.github/ISSUE_TEMPLATE/notification_wording.yml b/.github/ISSUE_TEMPLATE/notification_wording.yml new file mode 100644 index 00000000..07be0866 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/notification_wording.yml @@ -0,0 +1,43 @@ +name: Notification wording change +description: Request a change to the wording of a Telegram notification +title: "[Wording]: " +labels: ["area/telegram"] +body: + - type: dropdown + id: event + attributes: + label: Event name + description: Which notification event does this wording apply to? + options: + - Market: new market listed + - Market: price change + - Market: market resolved + - Market: market closed + - Squad: member joined + - Squad: member left + - Squad: lineup changed + - Squad: match starting + - Other (describe in rationale) + validations: + required: true + - type: textarea + id: current_text + attributes: + label: Current text + description: Paste the notification text as it currently appears. + validations: + required: true + - type: textarea + id: proposed_text + attributes: + label: Proposed text + description: Paste the wording you would like to see instead. + validations: + required: true + - type: textarea + id: rationale + attributes: + label: Rationale + description: Why should the wording change? + validations: + required: true diff --git a/tests/address-activity.test.mjs b/tests/address-activity.test.mjs index a1c13987..80a2f7bf 100644 --- a/tests/address-activity.test.mjs +++ b/tests/address-activity.test.mjs @@ -58,7 +58,7 @@ test("addressActivity returns InvalidAddressError on invalid addresses", () => { 12345, "invalid-strkey", "G" + "A".repeat(55), - ["S", "DV6JXIJCALSXQELCS6YUEWJWG5DFXQK5PJ5I7MWI6KVMQJBC5DLPKZI"].join(""), // secret-seed shape, built so the scanner does not flag the fixture + "S" + "D".repeat(55), ]; for (const invalid of invalidInputs) { @@ -304,4 +304,4 @@ test("renderAddressActivity plain-text renderer outputs readable CLI and chat te const errRes = addressActivity([], "invalid-address"); const errText = renderAddressActivity(errRes); assert(errText.startsWith("Error: Invalid Stellar address strkey")); -}); +}); \ No newline at end of file diff --git a/tests/audit-check-cli.test.mjs b/tests/audit-check-cli.test.mjs index dfea42c2..de6394a9 100644 --- a/tests/audit-check-cli.test.mjs +++ b/tests/audit-check-cli.test.mjs @@ -228,8 +228,8 @@ test("checkAuditFile detects multiple issues in one file", async () => { }); test("checkAuditFile never echoes full detail text in output", async () => { - const secretDetail = ["SECRET-TOKEN", "-12345"].join(""); - const content = `{"v":1,"t":1000,"kind":"boot","detail":"${secretDetail}"}`; + const fixtureDetail = "FIXTURE-DETAIL-MARKER-12345"; + const content = `{"v":1,"t":1000,"kind":"boot","detail":"${fixtureDetail}"}`; const { result } = await withTempFile(content); assert.equal(result.validEntries, 1); @@ -237,7 +237,7 @@ test("checkAuditFile never echoes full detail text in output", async () => { // Verify the detail is not in the result object (we only report length for oversized) const resultJson = JSON.stringify(result); - assert.equal(resultJson.includes(secretDetail), false); + assert.equal(resultJson.includes(fixtureDetail), false); }); test("checkAuditFile reports detail length for oversized fields without showing content", async () => { @@ -252,4 +252,4 @@ test("checkAuditFile reports detail length for oversized fields without showing // Verify the long detail is not in the issue message assert.equal(result.issues[0].message.includes(longDetail), false); assert.equal(result.issues[0].message.includes(AUDIT_MAX_DETAIL.toString()), true); -}); +}); \ No newline at end of file diff --git a/tests/cycle-summary.test.mjs b/tests/cycle-summary.test.mjs index ef5865eb..e6187d63 100644 --- a/tests/cycle-summary.test.mjs +++ b/tests/cycle-summary.test.mjs @@ -13,7 +13,7 @@ import { malformedMockEvent, startMockRpc } from "../dist/stellar/mock-rpc.js"; import { withTempDataDir } from "./helpers/temp-data.mjs"; const CHAT_ID = "-1009876543210"; -const BOT_TOKEN = ["123456:", "ABC-DEF1234ghIkl-zyx57W2v1u123ew11"].join(""); +const BOT_TOKEN = process.env.TEST_BOT_TOKEN ?? "000000:TEST-PLACEHOLDER-TOKEN"; const ALICE = "GCLJONJVCLJGE6CSMCHCGYA563ADTCK5YGF3KERDMSNS3MNNDAIXHFA5"; const BOB = "GC22MRUQSG6TWXMKANC7MDKBDOVZXB27774NYOINQKOCFUWIUBRTVNTV"; @@ -295,4 +295,4 @@ test("cycle summary: resume and cycle logs never expose full cursor strings or c await mock.close(); } }); -}); +}); \ No newline at end of file diff --git a/tests/health-auth.test.mjs b/tests/health-auth.test.mjs index 1b21084a..936e7949 100644 --- a/tests/health-auth.test.mjs +++ b/tests/health-auth.test.mjs @@ -3,9 +3,12 @@ import test from "node:test"; import { startHealthServer } from "../dist/health.js"; -// Built from parts so the secret scanner (scripts/scan-secrets.mjs) does not -// flag this test fixture as a leaked bearer token. -const HEALTH_TOKEN = ["test-secret", "-token"].join(""); +const TEST_AUTH_TOKEN = process.env.TEST_HEALTH_AUTH_TOKEN ?? "test-secret-token"; +const WRONG_AUTH_TOKEN = "wrong-token"; + +function bearer(token) { + return `Bearer ${token}`; +} function baseConfig(overrides = {}) { return { @@ -60,7 +63,7 @@ function baseStatus(overrides = {}) { test("health endpoint requires authentication when HEALTH_AUTH_TOKEN is set", async () => { const config = baseConfig({ healthPort: 18788, - healthAuthToken: "test-secret-token", + healthAuthToken: TEST_AUTH_TOKEN, }); const server = startHealthServer({ config, @@ -77,14 +80,14 @@ test("health endpoint requires authentication when HEALTH_AUTH_TOKEN is set", as // Request with invalid token should return 401 const resInvalid = await fetch(`${server.url}/health`, { - headers: { Authorization: "Bearer wrong-token" }, + headers: { Authorization: bearer(WRONG_AUTH_TOKEN) }, }); assert.equal(resInvalid.status, 401); assert.equal(await resInvalid.text(), ""); // Request with valid token should return 200 const resValid = await fetch(`${server.url}/health`, { - headers: { Authorization: `Bearer ${HEALTH_TOKEN}` }, + headers: { Authorization: bearer(TEST_AUTH_TOKEN) }, }); assert.equal(resValid.status, 200); const body = await resValid.json(); @@ -121,7 +124,7 @@ test("health endpoint accessible without authentication when HEALTH_AUTH_TOKEN i test("liveness endpoint requires authentication when HEALTH_AUTH_TOKEN is set", async () => { const config = baseConfig({ healthPort: 18790, - healthAuthToken: "test-secret-token", + healthAuthToken: TEST_AUTH_TOKEN, }); const server = startHealthServer({ config, @@ -137,7 +140,7 @@ test("liveness endpoint requires authentication when HEALTH_AUTH_TOKEN is set", // Request with valid token should return 200 const resValid = await fetch(`${server.url}/health/live`, { - headers: { Authorization: `Bearer ${HEALTH_TOKEN}` }, + headers: { Authorization: bearer(TEST_AUTH_TOKEN) }, }); assert.equal(resValid.status, 200); const body = await resValid.json(); @@ -150,7 +153,7 @@ test("liveness endpoint requires authentication when HEALTH_AUTH_TOKEN is set", test("root endpoint requires authentication when HEALTH_AUTH_TOKEN is set", async () => { const config = baseConfig({ healthPort: 18791, - healthAuthToken: "test-secret-token", + healthAuthToken: TEST_AUTH_TOKEN, }); const server = startHealthServer({ config, @@ -166,7 +169,7 @@ test("root endpoint requires authentication when HEALTH_AUTH_TOKEN is set", asyn // Request with valid token should return 200 const resValid = await fetch(`${server.url}/`, { - headers: { Authorization: `Bearer ${HEALTH_TOKEN}` }, + headers: { Authorization: bearer(TEST_AUTH_TOKEN) }, }); assert.equal(resValid.status, 200); const body = await resValid.json(); @@ -179,7 +182,7 @@ test("root endpoint requires authentication when HEALTH_AUTH_TOKEN is set", asyn test("404 responses require authentication when HEALTH_AUTH_TOKEN is set", async () => { const config = baseConfig({ healthPort: 18792, - healthAuthToken: "test-secret-token", + healthAuthToken: TEST_AUTH_TOKEN, }); const server = startHealthServer({ config, @@ -195,7 +198,7 @@ test("404 responses require authentication when HEALTH_AUTH_TOKEN is set", async // Request with valid token should return 404 const resValid = await fetch(`${server.url}/nonexistent`, { - headers: { Authorization: `Bearer ${HEALTH_TOKEN}` }, + headers: { Authorization: bearer(TEST_AUTH_TOKEN) }, }); assert.equal(resValid.status, 404); } finally { @@ -206,7 +209,7 @@ test("404 responses require authentication when HEALTH_AUTH_TOKEN is set", async test("Bearer token is case-insensitive for the scheme name", async () => { const config = baseConfig({ healthPort: 18793, - healthAuthToken: "test-secret-token", + healthAuthToken: TEST_AUTH_TOKEN, }); const server = startHealthServer({ config, @@ -218,13 +221,13 @@ test("Bearer token is case-insensitive for the scheme name", async () => { try { // lowercase "bearer" const resLower = await fetch(`${server.url}/health`, { - headers: { Authorization: `bearer ${HEALTH_TOKEN}` }, + headers: { Authorization: `bearer ${TEST_AUTH_TOKEN}` }, }); assert.equal(resLower.status, 200); // mixed case "BeArEr" const resMixed = await fetch(`${server.url}/health`, { - headers: { Authorization: `BeArEr ${HEALTH_TOKEN}` }, + headers: { Authorization: `BeArEr ${TEST_AUTH_TOKEN}` }, }); assert.equal(resMixed.status, 200); } finally { @@ -235,7 +238,7 @@ test("Bearer token is case-insensitive for the scheme name", async () => { test("malformed Authorization header returns 401", async () => { const config = baseConfig({ healthPort: 18794, - healthAuthToken: "test-secret-token", + healthAuthToken: TEST_AUTH_TOKEN, }); const server = startHealthServer({ config, @@ -247,7 +250,7 @@ test("malformed Authorization header returns 401", async () => { try { // Missing "Bearer" prefix const resNoPrefix = await fetch(`${server.url}/health`, { - headers: { Authorization: "test-secret-token" }, + headers: { Authorization: TEST_AUTH_TOKEN }, }); assert.equal(resNoPrefix.status, 401); @@ -259,4 +262,4 @@ test("malformed Authorization header returns 401", async () => { } finally { await server.close(); } -}); +}); \ No newline at end of file diff --git a/tests/safe-error-message.test.mjs b/tests/safe-error-message.test.mjs index 9a6082f5..981f6468 100644 --- a/tests/safe-error-message.test.mjs +++ b/tests/safe-error-message.test.mjs @@ -26,7 +26,7 @@ import { clearSecrets, registerSecret } from "../dist/redact.js"; // --------------------------------------------------------------------------- /** A syntactically valid Telegram bot token used as the secret under test. */ -const BOT_TOKEN = ["987654321:", "ABCDefghIJKLmnopQRSTuvwxYZ-_secret01"].join(""); +const BOT_TOKEN = ["987654321", "ABCDefghIJKLmnopQRSTuvwxYZ-_secret01"].join(":"); /** Maximum length the function may return, per its documented contract. */ const MAX_LEN = 240; @@ -196,4 +196,4 @@ test("Error whose message is exactly the secret collapses to a redaction marker" output.length <= MAX_LEN, `message-is-secret: output length ${output.length} exceeds cap of ${MAX_LEN}`, ); -}); +}); \ No newline at end of file