mirror of
https://github.com/tiennm99/claude-status-webhook.git
synced 2026-04-17 15:20:37 +00:00
fix: security and robustness improvements, add project docs
- Hash inputs in timingSafeEqual to prevent length leak side-channel - Add quote escaping to escapeHtml for defense in depth - Normalize chatId to Number in parseKvKey for type consistency - Log Retry-After header on 429 rate limit responses - Slim README to focused overview, move details to docs/ - Add docs/: system-architecture, setup-guide, feature-decisions - Add documentation section and README guidelines to CLAUDE.md
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
/**
|
||||
* Timing-safe string comparison using Web Crypto API
|
||||
* Timing-safe string comparison using Web Crypto API.
|
||||
* Hashes both inputs first so the comparison is always fixed-length (32 bytes),
|
||||
* preventing attackers from probing the secret length via timing side-channels.
|
||||
*/
|
||||
export async function timingSafeEqual(a, b) {
|
||||
const encoder = new TextEncoder();
|
||||
const bufA = encoder.encode(a);
|
||||
const bufB = encoder.encode(b);
|
||||
if (bufA.byteLength !== bufB.byteLength) return false;
|
||||
return crypto.subtle.timingSafeEqual(bufA, bufB);
|
||||
const [hashA, hashB] = await Promise.all([
|
||||
crypto.subtle.digest("SHA-256", encoder.encode(a)),
|
||||
crypto.subtle.digest("SHA-256", encoder.encode(b)),
|
||||
]);
|
||||
return crypto.subtle.timingSafeEqual(hashA, hashB);
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ function parseKvKey(kvKey) {
|
||||
const possibleThread = raw.slice(lastColon + 1);
|
||||
if (/^\d+$/.test(possibleThread)) {
|
||||
return {
|
||||
chatId: raw.slice(0, lastColon),
|
||||
chatId: Number(raw.slice(0, lastColon)),
|
||||
threadId: parseInt(possibleThread, 10),
|
||||
};
|
||||
}
|
||||
return { chatId: raw, threadId: null };
|
||||
return { chatId: Number(raw), threadId: null };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,7 +42,8 @@ export async function handleQueue(batch, env) {
|
||||
removed++;
|
||||
msg.ack();
|
||||
} else if (res.status === 429) {
|
||||
console.log("Queue: rate limited, retrying");
|
||||
const retryAfter = res.headers.get("Retry-After");
|
||||
console.log(`Queue: rate limited for ${chatId}, Retry-After: ${retryAfter ?? "unknown"}`);
|
||||
retried++;
|
||||
msg.retry();
|
||||
} else {
|
||||
|
||||
@@ -64,7 +64,7 @@ export function humanizeStatus(status) {
|
||||
* Escape HTML special chars for Telegram's HTML parse mode
|
||||
*/
|
||||
export function escapeHtml(s) {
|
||||
return s?.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") ?? "";
|
||||
return s?.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """) ?? "";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user