[{"data":1,"prerenderedAt":1470},["ShallowReactive",2],{"page-\u002Freact\u002F25-security":3},{"id":4,"title":5,"body":6,"description":1463,"extension":1464,"meta":1465,"navigation":52,"path":1466,"seo":1467,"stem":1468,"__hash__":1469},"content\u002Freact\u002F25-security.md","25 — Security",{"type":7,"value":8,"toc":1451},"minimark",[9,13,18,147,151,373,377,675,679,690,694,793,797,920,924,1023,1027,1136,1140,1148,1192,1195,1359,1363,1447],[10,11,5],"h1",{"id":12},"_25-security",[14,15,17],"h2",{"id":16},"reacts-automatic-xss-protection","React's Automatic XSS Protection",[19,20,23],"code-wrapper",{"filename":21,"language":22},"automatic_escaping.js","javascript",[24,25,29],"pre",{"className":26,"code":27,"language":22,"meta":28,"style":28},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F React automatically escapes all values rendered in {}.\n\u002F\u002F This prevents XSS for the overwhelming majority of rendering.\n\nconst userInput = '\u003Cscript>alert(\"xss\")\u003C\u002Fscript>'\n\nfunction SafeComponent() {\n  return \u003Cdiv>{userInput}\u003C\u002Fdiv>\n  \u002F\u002F Renders as literal text: \u003Cscript>alert(\"xss\")\u003C\u002Fscript>\n  \u002F\u002F The script is NOT executed — React escapes \u003C, >, &, \", ' in string values.\n}\n\n\u002F\u002F The escaping happens because React uses textContent (not innerHTML) for string\n\u002F\u002F children. The browser treats the content as text, not as HTML markup.\n","",[30,31,32,41,47,54,72,77,91,112,118,124,130,135,141],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sdCPZ","\u002F\u002F React automatically escapes all values rendered in {}.\n",[33,42,44],{"class":35,"line":43},2,[33,45,46],{"class":39},"\u002F\u002F This prevents XSS for the overwhelming majority of rendering.\n",[33,48,50],{"class":35,"line":49},3,[33,51,53],{"emptyLinePlaceholder":52},true,"\n",[33,55,57,61,65,68],{"class":35,"line":56},4,[33,58,60],{"class":59},"svdQ7","const",[33,62,64],{"class":63},"snvgF"," userInput",[33,66,67],{"class":59}," =",[33,69,71],{"class":70},"sJ6F3"," '\u003Cscript>alert(\"xss\")\u003C\u002Fscript>'\n",[33,73,75],{"class":35,"line":74},5,[33,76,53],{"emptyLinePlaceholder":52},[33,78,80,83,87],{"class":35,"line":79},6,[33,81,82],{"class":59},"function",[33,84,86],{"class":85},"sIsaT"," SafeComponent",[33,88,90],{"class":89},"ssxIu","() {\n",[33,92,94,97,100,104,107,109],{"class":35,"line":93},7,[33,95,96],{"class":59},"  return",[33,98,99],{"class":89}," \u003C",[33,101,103],{"class":102},"sk71V","div",[33,105,106],{"class":89},">{userInput}\u003C\u002F",[33,108,103],{"class":102},[33,110,111],{"class":89},">\n",[33,113,115],{"class":35,"line":114},8,[33,116,117],{"class":39},"  \u002F\u002F Renders as literal text: \u003Cscript>alert(\"xss\")\u003C\u002Fscript>\n",[33,119,121],{"class":35,"line":120},9,[33,122,123],{"class":39},"  \u002F\u002F The script is NOT executed — React escapes \u003C, >, &, \", ' in string values.\n",[33,125,127],{"class":35,"line":126},10,[33,128,129],{"class":89},"}\n",[33,131,133],{"class":35,"line":132},11,[33,134,53],{"emptyLinePlaceholder":52},[33,136,138],{"class":35,"line":137},12,[33,139,140],{"class":39},"\u002F\u002F The escaping happens because React uses textContent (not innerHTML) for string\n",[33,142,144],{"class":35,"line":143},13,[33,145,146],{"class":39},"\u002F\u002F children. The browser treats the content as text, not as HTML markup.\n",[14,148,150],{"id":149},"the-escape-hatch-dangerouslysetinnerhtml","The Escape Hatch: dangerouslySetInnerHTML",[19,152,154],{"filename":153,"language":22},"dangerously_set_inner_html.js",[24,155,157],{"className":26,"code":156,"language":22,"meta":28,"style":28},"\u002F\u002F ANTI-PATTERN: dangerouslySetInnerHTML with user-controlled content\nfunction UnsafeComment({ content }) {\n  return \u003Cdiv dangerouslySetInnerHTML={{ __html: content }} \u002F>\n  \u002F\u002F If `content` comes from a user and contains \u003Cimg onerror=alert(1) src=x>,\n  \u002F\u002F it EXECUTES the onerror handler. This is a direct XSS vector.\n}\n\n\u002F\u002F PRODUCTION: sanitize before injecting, using a trusted library\nimport DOMPurify from 'dompurify'\n\nfunction SafeComment({ content }) {\n  const clean = DOMPurify.sanitize(content, {\n    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],\n    ALLOWED_ATTR: ['href', 'target', 'rel'],\n  })\n  return \u003Cdiv dangerouslySetInnerHTML={{ __html: clean }} \u002F>\n  \u002F\u002F DOMPurify strips event handlers, javascript: URLs, and disallowed tags.\n  \u002F\u002F NEVER use dangerouslySetInnerHTML without sanitization — the name is a warning.\n}\n",[30,158,159,164,181,198,203,208,212,216,221,235,239,252,271,313,334,340,356,362,368],{"__ignoreMap":28},[33,160,161],{"class":35,"line":36},[33,162,163],{"class":39},"\u002F\u002F ANTI-PATTERN: dangerouslySetInnerHTML with user-controlled content\n",[33,165,166,168,171,174,178],{"class":35,"line":43},[33,167,82],{"class":59},[33,169,170],{"class":85}," UnsafeComment",[33,172,173],{"class":89},"({ ",[33,175,177],{"class":176},"sCrzJ","content",[33,179,180],{"class":89}," }) {\n",[33,182,183,185,187,189,192,195],{"class":35,"line":49},[33,184,96],{"class":59},[33,186,99],{"class":89},[33,188,103],{"class":102},[33,190,191],{"class":85}," dangerouslySetInnerHTML",[33,193,194],{"class":59},"=",[33,196,197],{"class":89},"{{ __html: content }} \u002F>\n",[33,199,200],{"class":35,"line":56},[33,201,202],{"class":39},"  \u002F\u002F If `content` comes from a user and contains \u003Cimg onerror=alert(1) src=x>,\n",[33,204,205],{"class":35,"line":74},[33,206,207],{"class":39},"  \u002F\u002F it EXECUTES the onerror handler. This is a direct XSS vector.\n",[33,209,210],{"class":35,"line":79},[33,211,129],{"class":89},[33,213,214],{"class":35,"line":93},[33,215,53],{"emptyLinePlaceholder":52},[33,217,218],{"class":35,"line":114},[33,219,220],{"class":39},"\u002F\u002F PRODUCTION: sanitize before injecting, using a trusted library\n",[33,222,223,226,229,232],{"class":35,"line":120},[33,224,225],{"class":59},"import",[33,227,228],{"class":89}," DOMPurify ",[33,230,231],{"class":59},"from",[33,233,234],{"class":70}," 'dompurify'\n",[33,236,237],{"class":35,"line":126},[33,238,53],{"emptyLinePlaceholder":52},[33,240,241,243,246,248,250],{"class":35,"line":132},[33,242,82],{"class":59},[33,244,245],{"class":85}," SafeComment",[33,247,173],{"class":89},[33,249,177],{"class":176},[33,251,180],{"class":89},[33,253,254,257,260,262,265,268],{"class":35,"line":137},[33,255,256],{"class":59},"  const",[33,258,259],{"class":63}," clean",[33,261,67],{"class":59},[33,263,264],{"class":89}," DOMPurify.",[33,266,267],{"class":85},"sanitize",[33,269,270],{"class":89},"(content, {\n",[33,272,273,276,279,282,285,287,290,292,295,297,300,302,305,307,310],{"class":35,"line":143},[33,274,275],{"class":89},"    ALLOWED_TAGS: [",[33,277,278],{"class":70},"'b'",[33,280,281],{"class":89},", ",[33,283,284],{"class":70},"'i'",[33,286,281],{"class":89},[33,288,289],{"class":70},"'em'",[33,291,281],{"class":89},[33,293,294],{"class":70},"'strong'",[33,296,281],{"class":89},[33,298,299],{"class":70},"'a'",[33,301,281],{"class":89},[33,303,304],{"class":70},"'p'",[33,306,281],{"class":89},[33,308,309],{"class":70},"'br'",[33,311,312],{"class":89},"],\n",[33,314,316,319,322,324,327,329,332],{"class":35,"line":315},14,[33,317,318],{"class":89},"    ALLOWED_ATTR: [",[33,320,321],{"class":70},"'href'",[33,323,281],{"class":89},[33,325,326],{"class":70},"'target'",[33,328,281],{"class":89},[33,330,331],{"class":70},"'rel'",[33,333,312],{"class":89},[33,335,337],{"class":35,"line":336},15,[33,338,339],{"class":89},"  })\n",[33,341,343,345,347,349,351,353],{"class":35,"line":342},16,[33,344,96],{"class":59},[33,346,99],{"class":89},[33,348,103],{"class":102},[33,350,191],{"class":85},[33,352,194],{"class":59},[33,354,355],{"class":89},"{{ __html: clean }} \u002F>\n",[33,357,359],{"class":35,"line":358},17,[33,360,361],{"class":39},"  \u002F\u002F DOMPurify strips event handlers, javascript: URLs, and disallowed tags.\n",[33,363,365],{"class":35,"line":364},18,[33,366,367],{"class":39},"  \u002F\u002F NEVER use dangerouslySetInnerHTML without sanitization — the name is a warning.\n",[33,369,371],{"class":35,"line":370},19,[33,372,129],{"class":89},[14,374,376],{"id":375},"url-href-injection","URL \u002F href Injection",[19,378,380],{"filename":379,"language":22},"href_injection.js",[24,381,383],{"className":26,"code":382,"language":22,"meta":28,"style":28},"\u002F\u002F ANTI-PATTERN: user-controlled href without validation\nfunction UserLink({ href, children }) {\n  return \u003Ca href={href}>{children}\u003C\u002Fa>\n  \u002F\u002F If href = \"javascript:alert(document.cookie)\" → clicking the link executes JS.\n  \u002F\u002F If href = \"data:text\u002Fhtml,\u003Cscript>alert(1)\u003C\u002Fscript>\" → same.\n}\n\n\u002F\u002F PRODUCTION: validate the protocol\nfunction SafeLink({ href, children, ...props }) {\n  \u002F\u002F Only allow http, https, mailto, tel — block javascript:, data:, vbscript:\n  const SAFE_PROTOCOLS = ['http:', 'https:', 'mailto:', 'tel:']\n  const url = new URL(href, window.location.origin)  \u002F\u002F resolve relative URLs\n  if (!SAFE_PROTOCOLS.includes(url.protocol)) {\n    return null  \u002F\u002F or render as plain text\n  }\n  \u002F\u002F Additional check: block data: URIs even if someone adds them to the allowlist\n  if (url.protocol === 'data:' && !href.startsWith('data:image\u002F')) {\n    return null\n  }\n  return \u003Ca href={href} rel=\"noopener noreferrer\" target=\"_blank\" {...props}>{children}\u003C\u002Fa>\n}\n",[30,384,385,390,409,430,435,440,444,448,453,478,483,516,537,560,571,576,581,615,622,626,670],{"__ignoreMap":28},[33,386,387],{"class":35,"line":36},[33,388,389],{"class":39},"\u002F\u002F ANTI-PATTERN: user-controlled href without validation\n",[33,391,392,394,397,399,402,404,407],{"class":35,"line":43},[33,393,82],{"class":59},[33,395,396],{"class":85}," UserLink",[33,398,173],{"class":89},[33,400,401],{"class":176},"href",[33,403,281],{"class":89},[33,405,406],{"class":176},"children",[33,408,180],{"class":89},[33,410,411,413,415,418,421,423,426,428],{"class":35,"line":49},[33,412,96],{"class":59},[33,414,99],{"class":89},[33,416,417],{"class":102},"a",[33,419,420],{"class":85}," href",[33,422,194],{"class":59},[33,424,425],{"class":89},"{href}>{children}\u003C\u002F",[33,427,417],{"class":102},[33,429,111],{"class":89},[33,431,432],{"class":35,"line":56},[33,433,434],{"class":39},"  \u002F\u002F If href = \"javascript:alert(document.cookie)\" → clicking the link executes JS.\n",[33,436,437],{"class":35,"line":74},[33,438,439],{"class":39},"  \u002F\u002F If href = \"data:text\u002Fhtml,\u003Cscript>alert(1)\u003C\u002Fscript>\" → same.\n",[33,441,442],{"class":35,"line":79},[33,443,129],{"class":89},[33,445,446],{"class":35,"line":93},[33,447,53],{"emptyLinePlaceholder":52},[33,449,450],{"class":35,"line":114},[33,451,452],{"class":39},"\u002F\u002F PRODUCTION: validate the protocol\n",[33,454,455,457,460,462,464,466,468,470,473,476],{"class":35,"line":120},[33,456,82],{"class":59},[33,458,459],{"class":85}," SafeLink",[33,461,173],{"class":89},[33,463,401],{"class":176},[33,465,281],{"class":89},[33,467,406],{"class":176},[33,469,281],{"class":89},[33,471,472],{"class":59},"...",[33,474,475],{"class":176},"props",[33,477,180],{"class":89},[33,479,480],{"class":35,"line":126},[33,481,482],{"class":39},"  \u002F\u002F Only allow http, https, mailto, tel — block javascript:, data:, vbscript:\n",[33,484,485,487,490,492,495,498,500,503,505,508,510,513],{"class":35,"line":132},[33,486,256],{"class":59},[33,488,489],{"class":63}," SAFE_PROTOCOLS",[33,491,67],{"class":59},[33,493,494],{"class":89}," [",[33,496,497],{"class":70},"'http:'",[33,499,281],{"class":89},[33,501,502],{"class":70},"'https:'",[33,504,281],{"class":89},[33,506,507],{"class":70},"'mailto:'",[33,509,281],{"class":89},[33,511,512],{"class":70},"'tel:'",[33,514,515],{"class":89},"]\n",[33,517,518,520,523,525,528,531,534],{"class":35,"line":137},[33,519,256],{"class":59},[33,521,522],{"class":63}," url",[33,524,67],{"class":59},[33,526,527],{"class":59}," new",[33,529,530],{"class":85}," URL",[33,532,533],{"class":89},"(href, window.location.origin)  ",[33,535,536],{"class":39},"\u002F\u002F resolve relative URLs\n",[33,538,539,542,545,548,551,554,557],{"class":35,"line":143},[33,540,541],{"class":59},"  if",[33,543,544],{"class":89}," (",[33,546,547],{"class":59},"!",[33,549,550],{"class":63},"SAFE_PROTOCOLS",[33,552,553],{"class":89},".",[33,555,556],{"class":85},"includes",[33,558,559],{"class":89},"(url.protocol)) {\n",[33,561,562,565,568],{"class":35,"line":315},[33,563,564],{"class":59},"    return",[33,566,567],{"class":63}," null",[33,569,570],{"class":39},"  \u002F\u002F or render as plain text\n",[33,572,573],{"class":35,"line":336},[33,574,575],{"class":89},"  }\n",[33,577,578],{"class":35,"line":342},[33,579,580],{"class":39},"  \u002F\u002F Additional check: block data: URIs even if someone adds them to the allowlist\n",[33,582,583,585,588,591,594,597,600,603,606,609,612],{"class":35,"line":358},[33,584,541],{"class":59},[33,586,587],{"class":89}," (url.protocol ",[33,589,590],{"class":59},"===",[33,592,593],{"class":70}," 'data:'",[33,595,596],{"class":59}," &&",[33,598,599],{"class":59}," !",[33,601,602],{"class":89},"href.",[33,604,605],{"class":85},"startsWith",[33,607,608],{"class":89},"(",[33,610,611],{"class":70},"'data:image\u002F'",[33,613,614],{"class":89},")) {\n",[33,616,617,619],{"class":35,"line":364},[33,618,564],{"class":59},[33,620,621],{"class":63}," null\n",[33,623,624],{"class":35,"line":370},[33,625,575],{"class":89},[33,627,629,631,633,635,637,639,642,645,647,650,653,655,658,661,663,666,668],{"class":35,"line":628},20,[33,630,96],{"class":59},[33,632,99],{"class":89},[33,634,417],{"class":102},[33,636,420],{"class":85},[33,638,194],{"class":59},[33,640,641],{"class":89},"{href} ",[33,643,644],{"class":85},"rel",[33,646,194],{"class":59},[33,648,649],{"class":70},"\"noopener noreferrer\"",[33,651,652],{"class":85}," target",[33,654,194],{"class":59},[33,656,657],{"class":70},"\"_blank\"",[33,659,660],{"class":89}," {",[33,662,472],{"class":59},[33,664,665],{"class":89},"props}>{children}\u003C\u002F",[33,667,417],{"class":102},[33,669,111],{"class":89},[33,671,673],{"class":35,"line":672},21,[33,674,129],{"class":89},[14,676,678],{"id":677},"content-security-policy-csp","Content Security Policy (CSP)",[19,680,683],{"filename":681,"language":682},"csp_header.txt","text",[24,684,688],{"className":685,"code":687,"language":682},[686],"language-text","# CSP header — defense in depth against XSS even if a sanitization bug exists.\n# Set as a response header: Content-Security-Policy: ...\n\n# STRICT (recommended for React apps):\ndefault-src 'self';\nscript-src 'self';\nstyle-src 'self' 'unsafe-inline';  # React injects styles inline — 'unsafe-inline' needed for styles\nimg-src 'self' data: https:\\;\nconnect-src 'self' https:\u002F\u002Fapi.example.com\\;\nfont-src 'self' https:\u002F\u002Ffonts.gstatic.com\\;\nframe-ancestors 'none';  # clickjacking protection\nbase-uri 'self';\nform-action 'self';\n\n# For apps using nonces (if you can't avoid inline scripts):\n# script-src 'self' 'nonce-{RANDOM}'\n# Each \u003Cscript> tag must include nonce=\"{RANDOM}\" — nonces are unique per request.\n",[30,689,687],{"__ignoreMap":28},[14,691,693],{"id":692},"dependency-auditing","Dependency Auditing",[19,695,698],{"filename":696,"language":697},"dependency_audit.sh","bash",[24,699,702],{"className":700,"code":701,"language":697,"meta":28,"style":28},"language-bash shiki shiki-themes github-light github-dark","# Check for known vulnerabilities in dependencies\nnpm audit\n\n# Fix automatically where possible\nnpm audit fix\n\n# Check for specific package: does it have known CVEs?\nnpm audit --audit-level=high\n\n# Use Snyk for deeper scanning (catches more than npm audit)\nnpx snyk test\n\n# Lockfile discipline: commit package-lock.json (or yarn.lock \u002F pnpm-lock.yaml)\n# to ensure CI and production install the EXACT same versions.\n# NEVER run `npm install` without --save-exact or caret ranges you haven't reviewed.\n",[30,703,704,709,717,721,726,736,740,745,754,758,763,774,778,783,788],{"__ignoreMap":28},[33,705,706],{"class":35,"line":36},[33,707,708],{"class":39},"# Check for known vulnerabilities in dependencies\n",[33,710,711,714],{"class":35,"line":43},[33,712,713],{"class":85},"npm",[33,715,716],{"class":70}," audit\n",[33,718,719],{"class":35,"line":49},[33,720,53],{"emptyLinePlaceholder":52},[33,722,723],{"class":35,"line":56},[33,724,725],{"class":39},"# Fix automatically where possible\n",[33,727,728,730,733],{"class":35,"line":74},[33,729,713],{"class":85},[33,731,732],{"class":70}," audit",[33,734,735],{"class":70}," fix\n",[33,737,738],{"class":35,"line":79},[33,739,53],{"emptyLinePlaceholder":52},[33,741,742],{"class":35,"line":93},[33,743,744],{"class":39},"# Check for specific package: does it have known CVEs?\n",[33,746,747,749,751],{"class":35,"line":114},[33,748,713],{"class":85},[33,750,732],{"class":70},[33,752,753],{"class":63}," --audit-level=high\n",[33,755,756],{"class":35,"line":120},[33,757,53],{"emptyLinePlaceholder":52},[33,759,760],{"class":35,"line":126},[33,761,762],{"class":39},"# Use Snyk for deeper scanning (catches more than npm audit)\n",[33,764,765,768,771],{"class":35,"line":132},[33,766,767],{"class":85},"npx",[33,769,770],{"class":70}," snyk",[33,772,773],{"class":70}," test\n",[33,775,776],{"class":35,"line":137},[33,777,53],{"emptyLinePlaceholder":52},[33,779,780],{"class":35,"line":143},[33,781,782],{"class":39},"# Lockfile discipline: commit package-lock.json (or yarn.lock \u002F pnpm-lock.yaml)\n",[33,784,785],{"class":35,"line":315},[33,786,787],{"class":39},"# to ensure CI and production install the EXACT same versions.\n",[33,789,790],{"class":35,"line":336},[33,791,792],{"class":39},"# NEVER run `npm install` without --save-exact or caret ranges you haven't reviewed.\n",[14,794,796],{"id":795},"client-side-security-limitations","Client-Side Security Limitations",[19,798,800],{"filename":799,"language":22},"client_limitations.js",[24,801,803],{"className":26,"code":802,"language":22,"meta":28,"style":28},"\u002F\u002F CRITICAL MINDSET: anything in the client bundle is PUBLIC.\n\u002F\u002F Never put secrets, API keys, or passwords in React source code.\n\n\u002F\u002F ANTI-PATTERN: API key in client-side code\nconst API_KEY = 'sk-live-1234567890'  \u002F\u002F ← visible in the browser bundle to anyone\nfetch(`https:\u002F\u002Fapi.example.com\u002Fdata?key=${API_KEY}`)\n\n\u002F\u002F PRODUCTION: proxy through your backend\nfetch('\u002Fapi\u002Fdata')  \u002F\u002F your server adds the API key server-side\n\u002F\u002F The key never reaches the browser. The browser only talks to YOUR server.\n\n\u002F\u002F Even \"hidden\" env vars (import.meta.env.VITE_API_KEY) are embedded in the\n\u002F\u002F client bundle at build time — they're not secrets, they're build-time constants.\n\u002F\u002F Only env vars WITHOUT the VITE_ prefix stay server-side in SSR frameworks.\n\n\u002F\u002F NEVER trust client-side validation alone — always validate on the server.\n\u002F\u002F Client validation is for UX (instant feedback), not security (trivially bypassed).\n",[30,804,805,810,815,819,824,839,858,862,867,882,887,891,896,901,906,910,915],{"__ignoreMap":28},[33,806,807],{"class":35,"line":36},[33,808,809],{"class":39},"\u002F\u002F CRITICAL MINDSET: anything in the client bundle is PUBLIC.\n",[33,811,812],{"class":35,"line":43},[33,813,814],{"class":39},"\u002F\u002F Never put secrets, API keys, or passwords in React source code.\n",[33,816,817],{"class":35,"line":49},[33,818,53],{"emptyLinePlaceholder":52},[33,820,821],{"class":35,"line":56},[33,822,823],{"class":39},"\u002F\u002F ANTI-PATTERN: API key in client-side code\n",[33,825,826,828,831,833,836],{"class":35,"line":74},[33,827,60],{"class":59},[33,829,830],{"class":63}," API_KEY",[33,832,67],{"class":59},[33,834,835],{"class":70}," 'sk-live-1234567890'",[33,837,838],{"class":39},"  \u002F\u002F ← visible in the browser bundle to anyone\n",[33,840,841,844,846,849,852,855],{"class":35,"line":79},[33,842,843],{"class":85},"fetch",[33,845,608],{"class":89},[33,847,848],{"class":70},"`https:\u002F\u002Fapi.example.com\u002Fdata?key=${",[33,850,851],{"class":63},"API_KEY",[33,853,854],{"class":70},"}`",[33,856,857],{"class":89},")\n",[33,859,860],{"class":35,"line":93},[33,861,53],{"emptyLinePlaceholder":52},[33,863,864],{"class":35,"line":114},[33,865,866],{"class":39},"\u002F\u002F PRODUCTION: proxy through your backend\n",[33,868,869,871,873,876,879],{"class":35,"line":120},[33,870,843],{"class":85},[33,872,608],{"class":89},[33,874,875],{"class":70},"'\u002Fapi\u002Fdata'",[33,877,878],{"class":89},")  ",[33,880,881],{"class":39},"\u002F\u002F your server adds the API key server-side\n",[33,883,884],{"class":35,"line":126},[33,885,886],{"class":39},"\u002F\u002F The key never reaches the browser. The browser only talks to YOUR server.\n",[33,888,889],{"class":35,"line":132},[33,890,53],{"emptyLinePlaceholder":52},[33,892,893],{"class":35,"line":137},[33,894,895],{"class":39},"\u002F\u002F Even \"hidden\" env vars (import.meta.env.VITE_API_KEY) are embedded in the\n",[33,897,898],{"class":35,"line":143},[33,899,900],{"class":39},"\u002F\u002F client bundle at build time — they're not secrets, they're build-time constants.\n",[33,902,903],{"class":35,"line":315},[33,904,905],{"class":39},"\u002F\u002F Only env vars WITHOUT the VITE_ prefix stay server-side in SSR frameworks.\n",[33,907,908],{"class":35,"line":336},[33,909,53],{"emptyLinePlaceholder":52},[33,911,912],{"class":35,"line":342},[33,913,914],{"class":39},"\u002F\u002F NEVER trust client-side validation alone — always validate on the server.\n",[33,916,917],{"class":35,"line":358},[33,918,919],{"class":39},"\u002F\u002F Client validation is for UX (instant feedback), not security (trivially bypassed).\n",[14,921,923],{"id":922},"tips-tricks","💡 Tips & Tricks",[19,925,927],{"filename":926,"language":22},"tips.js",[24,928,930],{"className":26,"code":929,"language":22,"meta":28,"style":28},"\u002F\u002F [Safety] React's automatic escaping covers 99% of XSS scenarios. The danger\n\u002F\u002F zones are: dangerouslySetInnerHTML, href={userInput}, and eval\u002FexecScript.\n\u002F\u002F Audit these three patterns regularly.\n\n\u002F\u002F [Idiom] For rich-text rendering from user input, use a library (DOMPurify,\n\u002F\u002F sanitize-html) — never try to write your own HTML sanitizer. Regex-based\n\u002F\u002F sanitizers are bypassable.\n\n\u002F\u002F [Safety] Add rel=\"noopener noreferrer\" to all target=\"_blank\" links. Without\n\u002F\u002F it, the opened page can access window.opener (your original window) — a\n\u002F\u002F reverse tabnabbing attack.\n\n\u002F\u002F [Debug] Check your production bundle for secrets: search the built JS files\n\u002F\u002F for any string that looks like a key, token, or password. If it's in the\n\u002F\u002F bundle, it's public.\n\n\u002F\u002F [Idiom] Use HTTP-only cookies for auth tokens instead of localStorage.\n\u002F\u002F HTTP-only cookies can't be read by JavaScript (document.cookie) — immune to\n\u002F\u002F XSS-based token theft. localStorage tokens are readable by any script on the page.\n",[30,931,932,937,942,947,951,956,961,966,970,975,980,985,989,994,999,1004,1008,1013,1018],{"__ignoreMap":28},[33,933,934],{"class":35,"line":36},[33,935,936],{"class":39},"\u002F\u002F [Safety] React's automatic escaping covers 99% of XSS scenarios. The danger\n",[33,938,939],{"class":35,"line":43},[33,940,941],{"class":39},"\u002F\u002F zones are: dangerouslySetInnerHTML, href={userInput}, and eval\u002FexecScript.\n",[33,943,944],{"class":35,"line":49},[33,945,946],{"class":39},"\u002F\u002F Audit these three patterns regularly.\n",[33,948,949],{"class":35,"line":56},[33,950,53],{"emptyLinePlaceholder":52},[33,952,953],{"class":35,"line":74},[33,954,955],{"class":39},"\u002F\u002F [Idiom] For rich-text rendering from user input, use a library (DOMPurify,\n",[33,957,958],{"class":35,"line":79},[33,959,960],{"class":39},"\u002F\u002F sanitize-html) — never try to write your own HTML sanitizer. Regex-based\n",[33,962,963],{"class":35,"line":93},[33,964,965],{"class":39},"\u002F\u002F sanitizers are bypassable.\n",[33,967,968],{"class":35,"line":114},[33,969,53],{"emptyLinePlaceholder":52},[33,971,972],{"class":35,"line":120},[33,973,974],{"class":39},"\u002F\u002F [Safety] Add rel=\"noopener noreferrer\" to all target=\"_blank\" links. Without\n",[33,976,977],{"class":35,"line":126},[33,978,979],{"class":39},"\u002F\u002F it, the opened page can access window.opener (your original window) — a\n",[33,981,982],{"class":35,"line":132},[33,983,984],{"class":39},"\u002F\u002F reverse tabnabbing attack.\n",[33,986,987],{"class":35,"line":137},[33,988,53],{"emptyLinePlaceholder":52},[33,990,991],{"class":35,"line":143},[33,992,993],{"class":39},"\u002F\u002F [Debug] Check your production bundle for secrets: search the built JS files\n",[33,995,996],{"class":35,"line":315},[33,997,998],{"class":39},"\u002F\u002F for any string that looks like a key, token, or password. If it's in the\n",[33,1000,1001],{"class":35,"line":336},[33,1002,1003],{"class":39},"\u002F\u002F bundle, it's public.\n",[33,1005,1006],{"class":35,"line":342},[33,1007,53],{"emptyLinePlaceholder":52},[33,1009,1010],{"class":35,"line":358},[33,1011,1012],{"class":39},"\u002F\u002F [Idiom] Use HTTP-only cookies for auth tokens instead of localStorage.\n",[33,1014,1015],{"class":35,"line":364},[33,1016,1017],{"class":39},"\u002F\u002F HTTP-only cookies can't be read by JavaScript (document.cookie) — immune to\n",[33,1019,1020],{"class":35,"line":370},[33,1021,1022],{"class":39},"\u002F\u002F XSS-based token theft. localStorage tokens are readable by any script on the page.\n",[14,1024,1026],{"id":1025},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[19,1028,1030],{"filename":1029,"language":22},"edge_cases.js",[24,1031,1033],{"className":26,"code":1032,"language":22,"meta":28,"style":28},"\u002F\u002F [Gotcha] dangerouslySetInnerHTML bypasses ALL of React's escaping. The prop\n\u002F\u002F name is a warning for a reason — it means \"I take responsibility for this\n\u002F\u002F being safe.\" Always sanitize with DOMPurify, never trust user content directly.\n\n\u002F\u002F [Gotcha] href injection works with ANY attribute that accepts a URL:\n\u002F\u002F \u003Ca href={x}>, \u003Cimg src={x}>, \u003Ciframe src={x}>, \u003Cform action={x}>, \u003Clink href={x}>.\n\u002F\u002F Validate the protocol on ALL of these, not just \u003Ca href>.\n\n\u002F\u002F [Gotcha] CSS injection via style={{...}} is limited (React uses inline styles,\n\u002F\u002F not CSS text), but className={userInput} can inject arbitrary class names.\n\u002F\u002F This isn't XSS, but it can enable CSS-based attacks (data exfiltration via\n\u002F\u002F attribute selectors + background-image URLs on internal-facing apps).\n\n\u002F\u002F [Gotcha] target=\"_blank\" without rel=\"noopener\" is a reverse tabnabbing vector.\n\u002F\u002F The opened page can redirect your original tab to a phishing site via\n\u002F\u002F window.opener.location = 'phishing.com'. React 18+ adds rel=\"noopener\" by\n\u002F\u002F default for target=\"_blank\" — but older versions don't.\n\n\u002F\u002F [Safety] PostMessage events: if your app uses window.postMessage for cross-origin\n\u002F\u002F communication, ALWAYS verify event.origin before trusting event.data.\n\u002F\u002F window.addEventListener('message', e => { if (e.origin !== 'https:\u002F\u002Ftrusted.com') return; ... })\n",[30,1034,1035,1040,1045,1050,1054,1059,1064,1069,1073,1078,1083,1088,1093,1097,1102,1107,1112,1117,1121,1126,1131],{"__ignoreMap":28},[33,1036,1037],{"class":35,"line":36},[33,1038,1039],{"class":39},"\u002F\u002F [Gotcha] dangerouslySetInnerHTML bypasses ALL of React's escaping. The prop\n",[33,1041,1042],{"class":35,"line":43},[33,1043,1044],{"class":39},"\u002F\u002F name is a warning for a reason — it means \"I take responsibility for this\n",[33,1046,1047],{"class":35,"line":49},[33,1048,1049],{"class":39},"\u002F\u002F being safe.\" Always sanitize with DOMPurify, never trust user content directly.\n",[33,1051,1052],{"class":35,"line":56},[33,1053,53],{"emptyLinePlaceholder":52},[33,1055,1056],{"class":35,"line":74},[33,1057,1058],{"class":39},"\u002F\u002F [Gotcha] href injection works with ANY attribute that accepts a URL:\n",[33,1060,1061],{"class":35,"line":79},[33,1062,1063],{"class":39},"\u002F\u002F \u003Ca href={x}>, \u003Cimg src={x}>, \u003Ciframe src={x}>, \u003Cform action={x}>, \u003Clink href={x}>.\n",[33,1065,1066],{"class":35,"line":93},[33,1067,1068],{"class":39},"\u002F\u002F Validate the protocol on ALL of these, not just \u003Ca href>.\n",[33,1070,1071],{"class":35,"line":114},[33,1072,53],{"emptyLinePlaceholder":52},[33,1074,1075],{"class":35,"line":120},[33,1076,1077],{"class":39},"\u002F\u002F [Gotcha] CSS injection via style={{...}} is limited (React uses inline styles,\n",[33,1079,1080],{"class":35,"line":126},[33,1081,1082],{"class":39},"\u002F\u002F not CSS text), but className={userInput} can inject arbitrary class names.\n",[33,1084,1085],{"class":35,"line":132},[33,1086,1087],{"class":39},"\u002F\u002F This isn't XSS, but it can enable CSS-based attacks (data exfiltration via\n",[33,1089,1090],{"class":35,"line":137},[33,1091,1092],{"class":39},"\u002F\u002F attribute selectors + background-image URLs on internal-facing apps).\n",[33,1094,1095],{"class":35,"line":143},[33,1096,53],{"emptyLinePlaceholder":52},[33,1098,1099],{"class":35,"line":315},[33,1100,1101],{"class":39},"\u002F\u002F [Gotcha] target=\"_blank\" without rel=\"noopener\" is a reverse tabnabbing vector.\n",[33,1103,1104],{"class":35,"line":336},[33,1105,1106],{"class":39},"\u002F\u002F The opened page can redirect your original tab to a phishing site via\n",[33,1108,1109],{"class":35,"line":342},[33,1110,1111],{"class":39},"\u002F\u002F window.opener.location = 'phishing.com'. React 18+ adds rel=\"noopener\" by\n",[33,1113,1114],{"class":35,"line":358},[33,1115,1116],{"class":39},"\u002F\u002F default for target=\"_blank\" — but older versions don't.\n",[33,1118,1119],{"class":35,"line":364},[33,1120,53],{"emptyLinePlaceholder":52},[33,1122,1123],{"class":35,"line":370},[33,1124,1125],{"class":39},"\u002F\u002F [Safety] PostMessage events: if your app uses window.postMessage for cross-origin\n",[33,1127,1128],{"class":35,"line":628},[33,1129,1130],{"class":39},"\u002F\u002F communication, ALWAYS verify event.origin before trusting event.data.\n",[33,1132,1133],{"class":35,"line":672},[33,1134,1135],{"class":39},"\u002F\u002F window.addEventListener('message', e => { if (e.origin !== 'https:\u002F\u002Ftrusted.com') return; ... })\n",[14,1137,1139],{"id":1138},"spot-the-bug","🧠 Spot the Bug",[1141,1142,1143,1144,1147],"p",{},"A blog renders user-submitted comments with rich text (bold, links). The developer uses ",[30,1145,1146],{},"dangerouslySetInnerHTML"," because the content comes from \"trusted\" admin users:",[19,1149,1151],{"filename":1150,"language":22},"spot_the_bug.js",[24,1152,1154],{"className":26,"code":1153,"language":22,"meta":28,"style":28},"function Comment({ content }) {\n  \u002F\u002F Content comes from admin users — \"trusted\"\n  return \u003Cdiv dangerouslySetInnerHTML={{ __html: content }} \u002F>\n}\n",[30,1155,1156,1169,1174,1188],{"__ignoreMap":28},[33,1157,1158,1160,1163,1165,1167],{"class":35,"line":36},[33,1159,82],{"class":59},[33,1161,1162],{"class":85}," Comment",[33,1164,173],{"class":89},[33,1166,177],{"class":176},[33,1168,180],{"class":89},[33,1170,1171],{"class":35,"line":43},[33,1172,1173],{"class":39},"  \u002F\u002F Content comes from admin users — \"trusted\"\n",[33,1175,1176,1178,1180,1182,1184,1186],{"class":35,"line":49},[33,1177,96],{"class":59},[33,1179,99],{"class":89},[33,1181,103],{"class":102},[33,1183,191],{"class":85},[33,1185,194],{"class":59},[33,1187,197],{"class":89},[33,1189,1190],{"class":35,"line":56},[33,1191,129],{"class":89},[1141,1193,1194],{},"What's the risk?",[1196,1197,1198,1202,1219,1226,1356],"details",{},[1199,1200,1201],"summary",{},"Answer",[1141,1203,1204,1205,1207,1208,281,1211,1214,1215,1218],{},"\"Trusted\" is not a security boundary — admin accounts can be compromised, and stored XSS in admin content affects every user who views the comment. If an attacker compromises an admin account (or an admin pastes content from an untrusted source), the ",[30,1206,177],{}," can contain ",[30,1209,1210],{},"\u003Cscript>",[30,1212,1213],{},"\u003Cimg onerror=...>",", or ",[30,1216,1217],{},"\u003Ca href=\"javascript:...\">"," tags that execute in every visitor's browser.",[1141,1220,1221,1225],{},[1222,1223,1224],"strong",{},"Fix",": sanitize with DOMPurify regardless of the source:",[24,1227,1229],{"className":26,"code":1228,"language":22,"meta":28,"style":28},"import DOMPurify from 'dompurify'\n\nfunction Comment({ content }) {\n  const clean = DOMPurify.sanitize(content, {\n    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br', 'ul', 'ol', 'li'],\n    ALLOWED_ATTR: ['href', 'target', 'rel'],\n  })\n  return \u003Cdiv dangerouslySetInnerHTML={{ __html: clean }} \u002F>\n}\n",[30,1230,1231,1241,1245,1257,1271,1318,1334,1338,1352],{"__ignoreMap":28},[33,1232,1233,1235,1237,1239],{"class":35,"line":36},[33,1234,225],{"class":59},[33,1236,228],{"class":89},[33,1238,231],{"class":59},[33,1240,234],{"class":70},[33,1242,1243],{"class":35,"line":43},[33,1244,53],{"emptyLinePlaceholder":52},[33,1246,1247,1249,1251,1253,1255],{"class":35,"line":49},[33,1248,82],{"class":59},[33,1250,1162],{"class":85},[33,1252,173],{"class":89},[33,1254,177],{"class":176},[33,1256,180],{"class":89},[33,1258,1259,1261,1263,1265,1267,1269],{"class":35,"line":56},[33,1260,256],{"class":59},[33,1262,259],{"class":63},[33,1264,67],{"class":59},[33,1266,264],{"class":89},[33,1268,267],{"class":85},[33,1270,270],{"class":89},[33,1272,1273,1275,1277,1279,1281,1283,1285,1287,1289,1291,1293,1295,1297,1299,1301,1303,1306,1308,1311,1313,1316],{"class":35,"line":74},[33,1274,275],{"class":89},[33,1276,278],{"class":70},[33,1278,281],{"class":89},[33,1280,284],{"class":70},[33,1282,281],{"class":89},[33,1284,289],{"class":70},[33,1286,281],{"class":89},[33,1288,294],{"class":70},[33,1290,281],{"class":89},[33,1292,299],{"class":70},[33,1294,281],{"class":89},[33,1296,304],{"class":70},[33,1298,281],{"class":89},[33,1300,309],{"class":70},[33,1302,281],{"class":89},[33,1304,1305],{"class":70},"'ul'",[33,1307,281],{"class":89},[33,1309,1310],{"class":70},"'ol'",[33,1312,281],{"class":89},[33,1314,1315],{"class":70},"'li'",[33,1317,312],{"class":89},[33,1319,1320,1322,1324,1326,1328,1330,1332],{"class":35,"line":79},[33,1321,318],{"class":89},[33,1323,321],{"class":70},[33,1325,281],{"class":89},[33,1327,326],{"class":70},[33,1329,281],{"class":89},[33,1331,331],{"class":70},[33,1333,312],{"class":89},[33,1335,1336],{"class":35,"line":93},[33,1337,339],{"class":89},[33,1339,1340,1342,1344,1346,1348,1350],{"class":35,"line":114},[33,1341,96],{"class":59},[33,1343,99],{"class":89},[33,1345,103],{"class":102},[33,1347,191],{"class":85},[33,1349,194],{"class":59},[33,1351,355],{"class":89},[33,1353,1354],{"class":35,"line":120},[33,1355,129],{"class":89},[1141,1357,1358],{},"The lesson: never bypass React's escaping without sanitization, regardless of the content source. \"Trusted\" users can be compromised, and stored XSS is the most dangerous type — it persists and affects every viewer.",[14,1360,1362],{"id":1361},"key-takeaways","Key Takeaways",[19,1364,1366],{"filename":1365,"language":22},"key_takeaways.js",[24,1367,1369],{"className":26,"code":1368,"language":22,"meta":28,"style":28},"\u002F\u002F 1. React automatically escapes all {} values — XSS is prevented by default.\n\u002F\u002F   The escape hatches that bypass this: dangerouslySetInnerHTML, href={url},\n\u002F\u002F   and eval\u002FexecScript. Audit these patterns regularly.\n\n\u002F\u002F 2. dangerouslySetInnerHTML REQUIRES sanitization (DOMPurify). The name is a\n\u002F\u002F    warning. \"Trusted\" source is not a security boundary — admins can be compromised.\n\n\u002F\u002F 3. URL injection: validate protocol on ALL URL attributes (href, src, action).\n\u002F\u002F    Block javascript:, data: (except images), vbscript:. Use new URL() to parse.\n\n\u002F\u002F 4. CSP headers are defense-in-depth — even if a sanitization bug exists, CSP\n\u002F\u002F    blocks inline script execution. Set script-src 'self' (or nonce-based).\n\n\u002F\u002F 5. Client bundle is PUBLIC — never put secrets in React source. Proxy through\n\u002F\u002F   your backend. Use HTTP-only cookies for auth tokens (immune to XSS theft).\n\u002F\u002F   Client-side validation is UX, not security — always validate on the server.\n",[30,1370,1371,1376,1381,1386,1390,1395,1400,1404,1409,1414,1418,1423,1428,1432,1437,1442],{"__ignoreMap":28},[33,1372,1373],{"class":35,"line":36},[33,1374,1375],{"class":39},"\u002F\u002F 1. React automatically escapes all {} values — XSS is prevented by default.\n",[33,1377,1378],{"class":35,"line":43},[33,1379,1380],{"class":39},"\u002F\u002F   The escape hatches that bypass this: dangerouslySetInnerHTML, href={url},\n",[33,1382,1383],{"class":35,"line":49},[33,1384,1385],{"class":39},"\u002F\u002F   and eval\u002FexecScript. Audit these patterns regularly.\n",[33,1387,1388],{"class":35,"line":56},[33,1389,53],{"emptyLinePlaceholder":52},[33,1391,1392],{"class":35,"line":74},[33,1393,1394],{"class":39},"\u002F\u002F 2. dangerouslySetInnerHTML REQUIRES sanitization (DOMPurify). The name is a\n",[33,1396,1397],{"class":35,"line":79},[33,1398,1399],{"class":39},"\u002F\u002F    warning. \"Trusted\" source is not a security boundary — admins can be compromised.\n",[33,1401,1402],{"class":35,"line":93},[33,1403,53],{"emptyLinePlaceholder":52},[33,1405,1406],{"class":35,"line":114},[33,1407,1408],{"class":39},"\u002F\u002F 3. URL injection: validate protocol on ALL URL attributes (href, src, action).\n",[33,1410,1411],{"class":35,"line":120},[33,1412,1413],{"class":39},"\u002F\u002F    Block javascript:, data: (except images), vbscript:. Use new URL() to parse.\n",[33,1415,1416],{"class":35,"line":126},[33,1417,53],{"emptyLinePlaceholder":52},[33,1419,1420],{"class":35,"line":132},[33,1421,1422],{"class":39},"\u002F\u002F 4. CSP headers are defense-in-depth — even if a sanitization bug exists, CSP\n",[33,1424,1425],{"class":35,"line":137},[33,1426,1427],{"class":39},"\u002F\u002F    blocks inline script execution. Set script-src 'self' (or nonce-based).\n",[33,1429,1430],{"class":35,"line":143},[33,1431,53],{"emptyLinePlaceholder":52},[33,1433,1434],{"class":35,"line":315},[33,1435,1436],{"class":39},"\u002F\u002F 5. Client bundle is PUBLIC — never put secrets in React source. Proxy through\n",[33,1438,1439],{"class":35,"line":336},[33,1440,1441],{"class":39},"\u002F\u002F   your backend. Use HTTP-only cookies for auth tokens (immune to XSS theft).\n",[33,1443,1444],{"class":35,"line":342},[33,1445,1446],{"class":39},"\u002F\u002F   Client-side validation is UX, not security — always validate on the server.\n",[1448,1449,1450],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}",{"title":28,"searchDepth":43,"depth":43,"links":1452},[1453,1454,1455,1456,1457,1458,1459,1460,1461,1462],{"id":16,"depth":43,"text":17},{"id":149,"depth":43,"text":150},{"id":375,"depth":43,"text":376},{"id":677,"depth":43,"text":678},{"id":692,"depth":43,"text":693},{"id":795,"depth":43,"text":796},{"id":922,"depth":43,"text":923},{"id":1025,"depth":43,"text":1026},{"id":1138,"depth":43,"text":1139},{"id":1361,"depth":43,"text":1362},"XSS in React, dangerouslySetInnerHTML, URL\u002Fhref injection, CSP headers, dependency auditing, and where React's automatic escaping ends. Code-first reference for mid-to-senior React engineers.","md",{},"\u002Freact\u002F25-security",{"title":5,"description":1463},"react\u002F25-security","3IlPW-WRfhLebCu86RcfJqpcb55fR_RbAEgcnBVpRnU",1789924652678]