{"record":{"id":"9ddb27464b875376","repo":"DIYgod/RSSHub","slug":"failed-to-solve-the-safeline-challenge-of-weather","errorCode":null,"errorMessage":"Failed to solve the SafeLine challenge of weather.cma.cn","messagePattern":"Failed to solve the SafeLine challenge of weather\\.cma\\.cn","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/cma/channel.tsx","lineNumber":23,"sourceCode":"import { renderToString } from 'hono/jsx/dom/server';\n\nimport type { Language, Route } from '@/types';\nimport got from '@/utils/got';\nimport ofetch from '@/utils/ofetch';\nimport { parseDate } from '@/utils/parse-date';\nimport timezone from '@/utils/timezone';\n\nconst solveChallenge = (prefix: string, leadingZeroBits: number) => {\n    for (let count = 0; count < 100_000_000; count++) {\n        const suffix = count.toString(16);\n        const hash = createHash('sha1')\n            .update(prefix + suffix)\n            .digest();\n        if (hash.readUInt32BE(0) >>> (32 - leadingZeroBits) === 0) {\n            return suffix;\n        }\n    }\n    throw new Error('Failed to solve the SafeLine challenge of weather.cma.cn');\n};\n\nconst fetchPageWithChallenge = async (url: string) => {\n    const response = await ofetch.raw<string>(url);\n    const html = response._data ?? '';\n    const challenge = response.headers\n        .getSetCookie()\n        .find((cookie) => cookie.startsWith('safeline_bot_challenge='))\n        ?.split(';', 1)[0];\n    const prefix = html.match(/var prefix = '(\\w+)';/)?.[1];\n    const leadingZeroBits = Number(html.match(/var leading_zero_bit = (\\d+);/)?.[1]);\n\n    if (!challenge || !prefix || !leadingZeroBits) {\n        return html;\n    }\n\n    const answer = challenge.replace('safeline_bot_challenge=', 'safeline_bot_challenge_ans=') + solveChallenge(prefix, leadingZeroBits);\n","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/cma/channel.tsx#L5-L41","documentation":"weather.cma.cn is protected by the SafeLine WAF, which serves a JavaScript proof-of-work challenge: find a hex suffix so that SHA1(prefix+suffix) starts with `leading_zero_bit` zero bits. The handler brute-forces up to 100,000,000 suffixes; if none satisfies the difficulty within that cap it throws a generic Error. This is a runtime/anti-bot failure, not bad user input.","triggerScenarios":"SafeLine raises `leading_zero_bit` so the 100M-iteration budget is exhausted; the page regexes (`var prefix = ...`, `var leading_zero_bit = ...`) no longer match because SafeLine changed its challenge HTML; the challenge cookie is stale.","commonSituations":"A SafeLine version bump changes the challenge page structure; a transient high-difficulty challenge; running on a slow CPU where the loop cannot complete in time.","solutions":["Retry the request — the challenge prefix and difficulty rotate, so a fresh page often solves quickly.","If the regexes stopped matching, inspect the served HTML and update the two match patterns in solveChallenge's caller.","Raise the iteration cap (the 100_000_000 literal) if difficulty is consistently high.","Report an upstream SafeLine format change to the route maintainer."],"exampleFix":"// before\n//   for (let count = 0; count < 100_000_000; count++) {\n// after\n//   for (let count = 0; count < 500_000_000; count++) {","handlingStrategy":"retry","validationCode":"// No caller-side validation prevents this; the challenge is server-issued.\n// Pre-flight: fetch the page once and confirm the two regexes match before solving.\nconst html = await ofetch.raw<string>(url);\nconst hasChallenge = /var prefix = '/.test(html._data ?? '') && /var leading_zero_bit = /.test(html._data ?? '');\nif (!hasChallenge) { /* expect solve to fail; skip or alert */ }","typeGuard":null,"tryCatchPattern":"try {\n  await fetchPageWithChallenge(url);\n} catch (e) {\n  // challenge difficulty/HTML may have rotated; one bounded retry often succeeds\n  await fetchPageWithChallenge(url);\n}","preventionTips":["Treat SafeLine challenge failures as transient; retry once before surfacing.","Monitor the two challenge regexes so an upstream format change is detected fast.","Keep the iteration cap proportional to expected worst-case difficulty."],"tags":["network","anti-bot","proof-of-work","retry"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}