fix(07-05): pay IN-01/IN-02/IN-03 — dead export gone, wrangler comment truthful, 900 s boundary pinned
- IN-01: delete the never-constructed ParsedInitData export (sole repo hit was its own declaration); the three read-helper catches now carry Intent: comments stating malformed-vs-tampered precedence and the chat-degrade law - IN-02: wrangler.jsonc line 2 states the post-fact truth — the chat_sessions namespace id below is live, no re-creation - IN-03: new T4 vector 'age 900 s → ok:true (the boundary is INCLUSIVE)' pins the strictly-greater comparator's inclusive edge (T-07-08); suite 126/126
This commit is contained in:
@@ -56,15 +56,6 @@ export interface WebAppInitChat {
|
|||||||
photo_url?: string;
|
photo_url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shape of a fully parsed initData payload (hash/signature excluded from
|
|
||||||
* rawFields — they are auth metadata, not payload fields). */
|
|
||||||
export interface ParsedInitData {
|
|
||||||
user: WebAppInitUser;
|
|
||||||
chat?: WebAppInitChat;
|
|
||||||
authDate: number;
|
|
||||||
rawFields: Map<string, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ValidateInitDataOptions {
|
export interface ValidateInitDataOptions {
|
||||||
/** "Now" in seconds-since-epoch — injectable for deterministic tests. */
|
/** "Now" in seconds-since-epoch — injectable for deterministic tests. */
|
||||||
nowSeconds?: number;
|
nowSeconds?: number;
|
||||||
@@ -95,8 +86,9 @@ function parseFields(raw: string): Map<string, string> {
|
|||||||
try {
|
try {
|
||||||
fields.set(key, decodeURIComponent(value));
|
fields.set(key, decodeURIComponent(value));
|
||||||
} catch {
|
} catch {
|
||||||
// Undecodable percent-escape — keep raw so HMAC compare fails ("tampered")
|
// Intent: an undecodable percent-escape keeps the RAW value so the HMAC
|
||||||
// rather than silently dropping the field.
|
// compare fails ("tampered") rather than silently dropping the field —
|
||||||
|
// malformed-vs-tampered precedence (D-14).
|
||||||
fields.set(key, value);
|
fields.set(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,6 +152,8 @@ export async function validateInitData(
|
|||||||
user = parsed as WebAppInitUser;
|
user = parsed as WebAppInitUser;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
// Intent: a malformed user payload is the "malformed" class, not a
|
||||||
|
// tampered one — the field never reaches the HMAC path.
|
||||||
user = undefined;
|
user = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,6 +203,8 @@ export async function validateInitData(
|
|||||||
chat = parsedChat as WebAppInitChat;
|
chat = parsedChat as WebAppInitChat;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
// Intent: a malformed chat payload degrades to "no chat context" (a
|
||||||
|
// private chat) — it never fails an otherwise-valid private-chat payload.
|
||||||
chat = undefined;
|
chat = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,6 +188,14 @@ test("T4 freshness boundary: age 901 s → reason stale", async () => {
|
|||||||
assert.equal(res.reason, "stale");
|
assert.equal(res.reason, "stale");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("T4 freshness boundary: age 900 s → ok:true (the boundary is INCLUSIVE)", async () => {
|
||||||
|
const validateInitData = await loadValidator();
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const raw = signedUser(now - 900);
|
||||||
|
const res = await validateInitData(raw, TEST_TOKEN);
|
||||||
|
assert.equal(res.ok, true, `expected ok:true at exactly 900 s, got ${JSON.stringify(res)}`);
|
||||||
|
});
|
||||||
|
|
||||||
/* ————— T5 — field order independence (RESEARCH Pitfall 1) ————— */
|
/* ————— T5 — field order independence (RESEARCH Pitfall 1) ————— */
|
||||||
|
|
||||||
test("T5 fields arriving out of alphabetical order still validate", async () => {
|
test("T5 fields arriving out of alphabetical order still validate", async () => {
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
// KV namespace id is filled in plan 03 task 1 after `npx wrangler kv namespace create chat_sessions`
|
// chat_sessions namespace created via `npx wrangler kv namespace create chat_sessions` — the id below is live.
|
||||||
"name": "araharah-bot",
|
"name": "araharah-bot",
|
||||||
"main": "src/webhook.ts",
|
"main": "src/webhook.ts",
|
||||||
"compatibility_date": "2026-10-22",
|
"compatibility_date": "2026-10-22",
|
||||||
|
|||||||
Reference in New Issue
Block a user