1import{j as e,S as g}from"./index-0qbe4j-K.js";import{P as y}from"./prose-CP3GO64z.js";import{b as f,L as m}from"./react-vendor-CuqwDvgQ.js";import{R as k}from"./index-DUfLO5S1.js";import{W as b}from"./widget-frame-BWiYKP5E.js";const d=[{key:"id",value:"8f2a-1c99",isPii:!1,reason:"An internal surrogate ID. Not identifying on its own, but it is what links every other field to one person."},{key:"firstName",value:"Jade",isPii:!0,reason:"A direct identifier. First name, last name and a birth date uniquely identify almost anyone."},{key:"lastName",value:"Okonkwo",isPii:!0,reason:"A direct identifier - the anchor for re-identifying a whole dataset."},{key:"email",value:"[email protected]",isPii:!0,reason:"A direct identifier, and the key people use to log in and reset passwords."},{key:"phone",value:"+44 7700 900123",isPii:!0,reason:"A direct identifier, and a favourite target for SMS phishing."},{key:"ip",value:"203.0.113.4",isPii:!0,reason:"An indirect identifier. An IP with a timestamp can locate a person or a household."},{key:"salaryBand",value:"£55â60k",isPii:!0,reason:"Financial information about a person - sensitive under most privacy laws."},{key:"deviceId",value:"Mozilla/5.0 ⦠Chrome/126",isPii:!0,reason:"Device identifiers are personal data too - tied to a personâs device and their browsing."},{key:"company",value:"Acme",isPii:!1,reason:"An employer alone rarely identifies a person. Combined with enough other fields it joins the re-identification picture."},{key:"jobTitle",value:"Engineer",isPii:!1,reason:"Neutral on its own; useful to an attacker only in aggregation."},{key:"lastLogin",value:"2026-08-05T14:22Z",isPii:!1,reason:"A timestamp is only sensitive because it is attached to a name."},{key:"isAdmin",value:"false",isPii:!1,reason:"A role flag - but the account it describes is the one attackers want."},{key:"notes",value:"Waiting on contract",isPii:!1,reason:"Free text is the wildcard: it is often where real names and account numbers end up."}];function p(){return d.filter(a=>a.isPii).map(a=>a.key)}function v(a){const o=new Set(p()),r=[...o].filter(n=>a.has(n)),c=[...o].filter(n=>!a.has(n)),u=d.filter(n=>!n.isPii&&a.has(n.key)).map(n=>n.key);return{found:r,missed:c,falsePositives:u,score:o.size===0?0:r.length/o.size}}function w(){const[a,o]=f.useState(new Set),[r,c]=f.useState(!1),u=t=>{r||o(i=>{const s=new Set(i);return s.has(t)?s.delete(t):s.add(t),s})},n=t=>{const i=d.find(h=>h.key===t),s=a.has(t);return r?i.isPii&&s?"correct":i.isPii&&!s?"missed":!i.isPii&&s?"false alarm":"correctly left alone":i.isPii?"is PII":"not PII"},l=v(a),x=p();return e.jsxs(b,{title:"Try it: which of these is personal data if it were real?",hint:"click every PII field",children:[e.jsxs("div",{className:"border-border bg-background mb-3 overflow-x-auto rounded-lg border p-3 font-mono text-xs",children:[e.jsx("span",{className:"text-muted-foreground",children:"{"}),e.jsx("div",{className:"ml-3 space-y-1",children:d.map(t=>{const i=a.has(t.key),s=t.isPii===i,h=!r||r&&!t.isPii&&!i;return e.jsxs("div",{className:"flex flex-wrap items-center gap-x-1.5",children:[e.jsxs("button",{type:"button",onClick:()=>u(t.key),"aria-pressed":i,className:`rounded px-0.5 text-left transition-colors ${h?i?"bg-primary/10 text-primary":"hover:bg-muted/60 text-foreground":s?"text-emerald-500":"text-red-500"}`,children:[e.jsxs("span",{className:"text-sky-500",children:['"',t.key,'"']}),': "',t.value,'"',t===d[d.length-1]?"":","]}),r&&e.jsx("span",{className:h?"text-muted-foreground text-[10px]":s?"text-[10px] text-emerald-500":"text-[10px] text-red-500",children:n(t.key)})]},t.key)})}),e.jsx("span",{className:"text-muted-foreground",children:"}"})]}),e.jsx("div",{className:"flex flex-wrap items-center gap-2",children:r?e.jsxs("button",{type:"button",onClick:()=>{c(!1),o(new Set)},className:"border-border hover:bg-muted inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-xs",children:[e.jsx(k,{className:"size-3.5"}),"Try again"]}):e.jsxs("button",{type:"button",onClick:()=>c(!0),className:"border-border hover:bg-muted inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-xs",children:[e.jsx(g,{className:"size-3.5"}),"Check answers"]})}),r&&e.jsxs("div",{className:"mt-3 space-y-1 text-xs",children:[e.jsxs("p",{children:["You found ",e.jsxs("strong",{children:[l.found.length," of ",x.length]}
1)," PII fields",l.score===1?" - every one. ":". ",l.falsePositives.length>0&&e.jsxs("span",{className:"text-amber-500",children:[l.falsePositives.join(", ")," flagged but they are not PII on their own."]})]}),l.missed.length>0&&e.jsxs("p",{className:"text-red-500",children:["Missed: ",l.missed.join(", "),". The reasons are usually the surprising ones - IP addresses and device IDs count."]})]})]})}function S(){return e.jsxs(y,{children:[e.jsx("p",{children:"Mock data is generated to look real, and that is exactly what makes it a liability. The whole point of a fake record is that it is convincing - and convincing fake data has a way of becoming real data: pasted into a demo, committed as a fixture, loaded into staging that feeds a production database, or embedded in documentation that outlives everyone who knows it is not real."}),e.jsx("h3",{children:"Real-looking data becomes real data"}),e.jsx("p",{children:'The failure mode is not the values themselves - it is the promotion. A row of "realistic" names, emails and phone numbers generated for QA reads as genuine to anyone downstream. A customer support agent who finds a record in the customer database has no reason to question it. A model trained on repositories treats the fixtures as facts. Once realistic mock data leaves the sandbox, you can no longer control which parts of it got treated as real.'}),e.jsx("p",{children:"The other direction is worse: mock data built to match a real product's schema ends up mirroring real fields - and the schema teaches you which of those fields would be personal data if the record were real."}),e.jsx(w,{}),e.jsx("p",{children:"The point of the exercise: if you would not put a field in a real database without controls, do not put a realistic version of it in your mock data either. The schema is a plan for the data you intend to hold."}),e.jsx("h3",{children:"The card number that passes the check"}),e.jsx("p",{children:'Test card numbers are Luhn-valid - they pass the checksum every payment form runs before it even calls a processor. That is convenient for development and a quiet signal of a problem: if your pipeline accepts a "card" purely because it passes a checksum, then a Luhn-valid fake and a stolen real number look identical to it. Validation that shallow is not validation, and your test fixtures are demonstrating it.'}),e.jsx("h3",{children:"What to do instead"}),e.jsxs("p",{children:["None of this argues against generating mock data; it argues for keeping it visibly fake and keeping the pipeline honest. The ",e.jsx(m,{to:"/tools/mock-data-generator",children:"Mock Data Generator"})," produces values that are clearly synthetic - and that is the feature. Use generator patterns like ",e.jsx("code",{children:"[email protected]"})," rather than names that could be a real customer's."]}),e.jsxs("p",{children:["Where mock data must carry real-looking content, scrub it before it leaves the machine with the ",e.jsx(m,{to:"/tools/scrub-for-ai",children:"Scrub for AI"})," tool, and run the"," ",e.jsx(m,{to:"/tools/repo-secret-scanner",children:"Repo Secret Scanner"})," over fixtures the same way you would over production config - the same discipline, because the fixture is one copy paste away from being a credential."]}),e.jsx("h3",{children:"A rule that scales"}),e.jsx("p",{children:"If a piece of mock data would be embarrassing or regulated if it were real, then generating a realistic version of it is building the incident before the product exists. Make the fake obviously fake, and never let the schema of realistic-looking records become the default shape of your real data."})]})}
1export{S as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.