[{"data":1,"prerenderedAt":425},["ShallowReactive",2],{"content-\u002Fdocs\u002Fpersistence\u002Fsensitive-names":3},{"id":4,"title":5,"body":6,"description":407,"extension":408,"meta":409,"metaRows":410,"navigation":244,"path":421,"seo":422,"source":420,"stem":423,"__hash__":424},"docs\u002Fdocs\u002Fpersistence\u002Fsensitive-names.md","Sensitive-name protection",{"type":7,"value":8,"toc":400},"minimark",[9,13,29,32,35,40,46,56,59,124,145,149,155,195,198,208,212,218,302,309,313,320,366,369,373,396],[10,11,5],"h1",{"id":12},"sensitive-name-protection",[14,15,16],"blockquote",{},[17,18,19,20,24,25,28],"p",{},"A speed bump for the obvious mistake: opting ",[21,22,23],"code",{},"password"," into client-side storage warns and skips the opt-in. The secret stays unwritten, which is the safe default. Pass ",[21,26,27],{},"acknowledgeSensitive: true"," if the client-side persistence is intentional.",[30,31],"docs-meta-table",{},[17,33,34],{},"Attaform never throws over a recoverable misconfiguration like this. A sensitive-named persist opt-in lands as a one-shot dev warning, the field is dropped from the persist set, and the form mounts cleanly. Devs see the warning during testing and either acknowledge or remove the opt-in; production users never see anything unsafe.",[36,37,39],"h2",{"id":38},"the-default-list","The default list",[17,41,42,45],{},[21,43,44],{},"DEFAULT_SENSITIVE_NAMES"," covers the common shapes: passwords, card data, identifiers, tokens, MFA artifacts.",[47,48,54],"pre",{"className":49,"code":51,"language":52,"meta":53},[50],"language-text","password, passwd, pwd, pin, cvv, cvc, ssn, social-security, dob,\ndate-of-birth, card-number, card, iban, routing-number,\naccount-number, passport, driver-license, mfa-secret, recovery-code,\ntoken, secret, api-key, private-key\n","text","",[21,55,51],{"__ignoreMap":53},[17,57,58],{},"Opting one of them into persistence is a quiet no-op plus a console warning:",[47,60,64],{"className":61,"code":62,"language":63,"meta":53,"style":53},"language-vue shiki shiki-themes github-light github-dark","\u003C!-- Warns once in dev; the field is not persisted. -->\n\u003Cinput v-register=\"form.register('password', { persist: true })\" \u002F>\n","vue",[21,65,66,75],{"__ignoreMap":53},[67,68,71],"span",{"class":69,"line":70},"line",1,[67,72,74],{"class":73},"sJ8bj","\u003C!-- Warns once in dev; the field is not persisted. -->\n",[67,76,78,82,86,90,93,97,100,103,106,109,112,116,119,121],{"class":69,"line":77},2,[67,79,81],{"class":80},"sVt8B","\u003C",[67,83,85],{"class":84},"s9eBZ","input",[67,87,89],{"class":88},"sScJk"," v-register",[67,91,92],{"class":80},"=",[67,94,96],{"class":95},"sZZnC","\"",[67,98,99],{"class":80},"form.",[67,101,102],{"class":88},"register",[67,104,105],{"class":80},"(",[67,107,108],{"class":95},"'password'",[67,110,111],{"class":80},", { persist: ",[67,113,115],{"class":114},"sj4cs","true",[67,117,118],{"class":80}," })",[67,120,96],{"class":95},[67,122,123],{"class":80}," \u002F>\n",[17,125,126,127,130,131,130,134,137,138,141,142,144],{},"The match is case-insensitive and ignores separator punctuation: ",[21,128,129],{},"apiKey",", ",[21,132,133],{},"api_key",[21,135,136],{},"api-key",", and ",[21,139,140],{},"API.KEY"," all hit the same ",[21,143,136],{}," entry. Slash-separated segments and camelCase boundaries split into words before comparison.",[36,146,148],{"id":147},"acknowledging-per-field","Acknowledging per field",[17,150,151,152,154],{},"When persistence is intentional (custom encrypted adapter, narrow-scope internal tool, server-managed encryption layer), pass ",[21,153,27],{},":",[47,156,158],{"className":61,"code":157,"language":63,"meta":53,"style":53},"\u003Cinput v-register=\"form.register('password', { persist: true, acknowledgeSensitive: true })\" \u002F>\n",[21,159,160],{"__ignoreMap":53},[67,161,162,164,166,168,170,172,174,176,178,180,182,184,187,189,191,193],{"class":69,"line":70},[67,163,81],{"class":80},[67,165,85],{"class":84},[67,167,89],{"class":88},[67,169,92],{"class":80},[67,171,96],{"class":95},[67,173,99],{"class":80},[67,175,102],{"class":88},[67,177,105],{"class":80},[67,179,108],{"class":95},[67,181,111],{"class":80},[67,183,115],{"class":114},[67,185,186],{"class":80},", acknowledgeSensitive: ",[67,188,115],{"class":114},[67,190,118],{"class":80},[67,192,96],{"class":95},[67,194,123],{"class":80},[17,196,197],{},"The acknowledgement silences the warning for that exact register call and lets the value through to storage. It does NOT remove the path from the resolved sensitive-names list, so the same path stays stripped from multi-tab broadcasts.",[17,199,200,201,203,204,207],{},"Treat ",[21,202,27],{}," as a code-review trigger, not a soundness boundary. The heuristic doesn't catch alias-typed paths (",[21,205,206],{},"register('pswd' as 'password')","), abbreviated variants not in the list, or schemas with deliberately innocuous keys for sensitive data. Per-field opt-in is the real defense; this is a default to lean against.",[36,209,211],{"id":210},"extending-the-list","Extending the list",[17,213,214,215,217],{},"The default is exposed as ",[21,216,44],{},". Compose your own by spreading it:",[47,219,223],{"className":220,"code":221,"language":222,"meta":53,"style":53},"language-ts shiki shiki-themes github-light github-dark","import { createAttaform, DEFAULT_SENSITIVE_NAMES } from 'attaform'\n\ncreateAttaform({\n  defaults: {\n    sensitiveNames: [...DEFAULT_SENSITIVE_NAMES, 'mrn', 'tax_id', 'health_record'],\n  },\n})\n","ts",[21,224,225,240,246,255,261,290,296],{"__ignoreMap":53},[67,226,227,231,234,237],{"class":69,"line":70},[67,228,230],{"class":229},"szBVR","import",[67,232,233],{"class":80}," { createAttaform, DEFAULT_SENSITIVE_NAMES } ",[67,235,236],{"class":229},"from",[67,238,239],{"class":95}," 'attaform'\n",[67,241,242],{"class":69,"line":77},[67,243,245],{"emptyLinePlaceholder":244},true,"\n",[67,247,249,252],{"class":69,"line":248},3,[67,250,251],{"class":88},"createAttaform",[67,253,254],{"class":80},"({\n",[67,256,258],{"class":69,"line":257},4,[67,259,260],{"class":80},"  defaults: {\n",[67,262,264,267,270,272,274,277,279,282,284,287],{"class":69,"line":263},5,[67,265,266],{"class":80},"    sensitiveNames: [",[67,268,269],{"class":229},"...",[67,271,44],{"class":114},[67,273,130],{"class":80},[67,275,276],{"class":95},"'mrn'",[67,278,130],{"class":80},[67,280,281],{"class":95},"'tax_id'",[67,283,130],{"class":80},[67,285,286],{"class":95},"'health_record'",[67,288,289],{"class":80},"],\n",[67,291,293],{"class":69,"line":292},6,[67,294,295],{"class":80},"  },\n",[67,297,299],{"class":69,"line":298},7,[67,300,301],{"class":80},"})\n",[17,303,304,305,308],{},"The resolved list applies to every form created by that app. Per-form overrides land via ",[21,306,307],{},"useForm({ sensitiveNames })",": the same union type, same matching rules.",[36,310,312],{"id":311},"one-list-two-surfaces","One list, two surfaces",[17,314,315,316,319],{},"The resolved ",[21,317,318],{},"sensitiveNames"," list gates two write paths:",[321,322,323,351],"ul",{},[324,325,326,330,331,334,335,338,339,342,343,346,347,350],"li",{},[327,328,329],"strong",{},"Persistence",": ",[21,332,333],{},"{ persist: true }"," on a sensitive path warns and skips the opt-in. Container opt-ins shed nested sensitive leaves the same way (",[21,336,337],{},"register('payment', { persist: true })"," writes ",[21,340,341],{},"payment.last4"," but not ",[21,344,345],{},"payment.cvv",", even if no one ever directly registered ",[21,348,349],{},"cvv","). The field stays out of storage unless you acknowledge it.",[324,352,353,356,357,361,362,365],{},[327,354,355],{},"Multi-tab sync",": matching paths are stripped ",[358,359,360],"em",{},"outbound"," (the broadcaster never posts them) AND rejected ",[358,363,364],{},"inbound"," (receivers drop them even if a peer tries to slip them through).",[17,367,368],{},"DevTools renders raw values by design. It is a dev-only surface, and redacting across every place a value surfaces (panels, logs, network tabs, breakpoints, source maps) is impractical security theater, not a real safeguard. Don't share your screen with the DevTools panel open over a customer's session.",[36,370,372],{"id":371},"where-to-next","Where to next",[321,374,375,383,390],{},[324,376,377,382],{},[378,379,381],"a",{"href":380},"\u002Fdocs\u002Fpersistence\u002Fper-field-opt-in","Per-field opt-in",": the deliberate per-path opt-in this heuristic backstops.",[324,384,385,389],{},[378,386,388],{"href":387},"\u002Fdocs\u002Fpersistence\u002Fstorage-backends","Storage backends",": the form-level gate before the field gate even matters.",[324,391,392,395],{},[378,393,355],{"href":394},"\u002Fdocs\u002Fcross-cutting-state\u002Fmulti-tab-sync",": the second subsystem that uses the same resolved list.",[397,398,399],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":53,"searchDepth":77,"depth":77,"links":401},[402,403,404,405,406],{"id":38,"depth":77,"text":39},{"id":147,"depth":77,"text":148},{"id":210,"depth":77,"text":211},{"id":311,"depth":77,"text":312},{"id":371,"depth":77,"text":372},"A built-in heuristic warns and skips the persist opt-in for paths like password \u002F cvv \u002F ssn. One configurable list gates persistence writes and multi-tab broadcasts.","md",{},[411,414,416],{"label":412,"value":413},"Category","Module",{"label":415,"value":44,"kind":21},"Default list",{"label":417,"value":418},"Override",{"createAttaform({ defaults":419},{"sensitiveNames":420},null,"\u002Fdocs\u002Fpersistence\u002Fsensitive-names",{"title":5,"description":407},"docs\u002Fpersistence\u002Fsensitive-names","Be-r46wh2g_kSXDLHJVvym3IPh34falisv73tYbvbxs",1780949760992]