safeseed 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/catalog.d.ts +77 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +203 -0
- package/dist/catalog.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +228 -0
- package/dist/cli.js.map +1 -0
- package/dist/csv.d.ts +21 -0
- package/dist/csv.d.ts.map +1 -0
- package/dist/csv.js +85 -0
- package/dist/csv.js.map +1 -0
- package/dist/generate.d.ts +28 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +101 -0
- package/dist/generate.js.map +1 -0
- package/dist/hash.d.ts +10 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +16 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/luhn.d.ts +8 -0
- package/dist/luhn.d.ts.map +1 -0
- package/dist/luhn.js +24 -0
- package/dist/luhn.js.map +1 -0
- package/dist/net.d.ts +16 -0
- package/dist/net.d.ts.map +1 -0
- package/dist/net.js +95 -0
- package/dist/net.js.map +1 -0
- package/dist/record.d.ts +36 -0
- package/dist/record.d.ts.map +1 -0
- package/dist/record.js +53 -0
- package/dist/record.js.map +1 -0
- package/dist/rng.d.ts +13 -0
- package/dist/rng.d.ts.map +1 -0
- package/dist/rng.js +26 -0
- package/dist/rng.js.map +1 -0
- package/dist/scan.d.ts +24 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +49 -0
- package/dist/scan.js.map +1 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/verify.d.ts +33 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +177 -0
- package/dist/verify.js.map +1 -0
- package/package.json +60 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SafeSeed CLI — the shell CI actually calls.
|
|
4
|
+
*
|
|
5
|
+
* safeseed generate --fields email:email,phone:phone --rows 100 --seed 42 \
|
|
6
|
+
* --out data.csv --record record.json
|
|
7
|
+
* safeseed verify --in data.csv --record record.json # exits non-zero on drift
|
|
8
|
+
* safeseed verify --in data.csv --record record.json --allow-added-columns
|
|
9
|
+
* # column-scoped: attest the
|
|
10
|
+
* # synthetic columns, report added ones
|
|
11
|
+
* safeseed scan --in legacy.csv --fields email:email,phone:phone
|
|
12
|
+
* safeseed catalog # print the reserved-range catalog
|
|
13
|
+
*
|
|
14
|
+
* No network, no config beyond the files you point it at. Exit codes: 0 clean,
|
|
15
|
+
* 1 drift/findings, 2 usage/IO error.
|
|
16
|
+
*/
|
|
17
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
18
|
+
import process from "node:process";
|
|
19
|
+
import { generate, toCsv, makeRunRecord, verify, scan, exitCode, CATALOG, CATALOG_VERSION, SAFESEED_VERSION, } from "./index.js";
|
|
20
|
+
function parseArgs(argv) {
|
|
21
|
+
const positional = [];
|
|
22
|
+
const flags = {};
|
|
23
|
+
for (let i = 0; i < argv.length; i++) {
|
|
24
|
+
const a = argv[i];
|
|
25
|
+
if (a.startsWith("--")) {
|
|
26
|
+
const eq = a.indexOf("=");
|
|
27
|
+
if (eq >= 0) {
|
|
28
|
+
flags[a.slice(2, eq)] = a.slice(eq + 1);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const key = a.slice(2);
|
|
32
|
+
const next = argv[i + 1];
|
|
33
|
+
if (next !== undefined && !next.startsWith("--")) {
|
|
34
|
+
flags[key] = next;
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
flags[key] = true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
positional.push(a);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { _: positional, flags };
|
|
47
|
+
}
|
|
48
|
+
function fail(msg) {
|
|
49
|
+
process.stderr.write(`safeseed: ${msg}\n`);
|
|
50
|
+
process.exit(2);
|
|
51
|
+
}
|
|
52
|
+
function reqStr(p, key) {
|
|
53
|
+
const v = p.flags[key];
|
|
54
|
+
if (typeof v !== "string" || v === "")
|
|
55
|
+
fail(`missing required --${key}`);
|
|
56
|
+
return v;
|
|
57
|
+
}
|
|
58
|
+
const VALID_TYPES = new Set(CATALOG.map((e) => e.field));
|
|
59
|
+
function parseFields(spec) {
|
|
60
|
+
return spec.split(",").map((pair) => {
|
|
61
|
+
const [name, type] = pair.split(":").map((s) => s.trim());
|
|
62
|
+
if (!name || !type)
|
|
63
|
+
fail(`bad --fields entry "${pair}" (expected name:type)`);
|
|
64
|
+
if (!VALID_TYPES.has(type))
|
|
65
|
+
fail(`unknown field type "${type}" (run: safeseed catalog)`);
|
|
66
|
+
return { name, type: type };
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function cmdGenerate(p) {
|
|
70
|
+
let schema = [];
|
|
71
|
+
let rows = 0;
|
|
72
|
+
let seed = 0;
|
|
73
|
+
let formatValid = true;
|
|
74
|
+
if (typeof p.flags.config === "string") {
|
|
75
|
+
const cfg = JSON.parse(readFileSync(p.flags.config, "utf8"));
|
|
76
|
+
if (cfg.schema)
|
|
77
|
+
schema = cfg.schema;
|
|
78
|
+
if (typeof cfg.rows === "number")
|
|
79
|
+
rows = cfg.rows;
|
|
80
|
+
if (typeof cfg.seed === "number")
|
|
81
|
+
seed = cfg.seed;
|
|
82
|
+
if (typeof cfg.formatValid === "boolean")
|
|
83
|
+
formatValid = cfg.formatValid;
|
|
84
|
+
}
|
|
85
|
+
if (typeof p.flags.fields === "string")
|
|
86
|
+
schema = parseFields(p.flags.fields);
|
|
87
|
+
if (p.flags.rows !== undefined)
|
|
88
|
+
rows = Number(p.flags.rows);
|
|
89
|
+
if (p.flags.seed !== undefined)
|
|
90
|
+
seed = Number(p.flags.seed);
|
|
91
|
+
if (p.flags["format-valid"] !== undefined)
|
|
92
|
+
formatValid = p.flags["format-valid"] !== "false";
|
|
93
|
+
if (schema.length === 0)
|
|
94
|
+
fail("no schema (use --config <file> or --fields name:type,...)");
|
|
95
|
+
if (!Number.isInteger(rows) || rows <= 0)
|
|
96
|
+
fail("--rows must be a positive integer");
|
|
97
|
+
if (!Number.isFinite(seed))
|
|
98
|
+
fail("--seed must be a number");
|
|
99
|
+
const ds = generate({ schema, rows, seed, formatValid });
|
|
100
|
+
const csv = toCsv(ds.columns, ds.rows);
|
|
101
|
+
if (typeof p.flags.out === "string")
|
|
102
|
+
writeFileSync(p.flags.out, csv);
|
|
103
|
+
else
|
|
104
|
+
process.stdout.write(csv);
|
|
105
|
+
const recordWork = typeof p.flags.record === "string"
|
|
106
|
+
? makeRunRecord(ds, csv, typeof p.flags.now === "string" ? { generatedAt: p.flags.now } : undefined).then((rec) => {
|
|
107
|
+
writeFileSync(p.flags.record, JSON.stringify(rec, null, 2) + "\n");
|
|
108
|
+
})
|
|
109
|
+
: Promise.resolve();
|
|
110
|
+
return recordWork.then(() => {
|
|
111
|
+
if (typeof p.flags.out === "string") {
|
|
112
|
+
process.stderr.write(`safeseed: wrote ${ds.rows.length} rows to ${p.flags.out}\n`);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function boolFlag(p, key) {
|
|
117
|
+
const v = p.flags[key];
|
|
118
|
+
return v === true || v === "true";
|
|
119
|
+
}
|
|
120
|
+
async function cmdVerify(p) {
|
|
121
|
+
const csv = readFileSync(reqStr(p, "in"), "utf8");
|
|
122
|
+
const record = JSON.parse(readFileSync(reqStr(p, "record"), "utf8"));
|
|
123
|
+
const allowAddedColumns = boolFlag(p, "allow-added-columns");
|
|
124
|
+
const result = await verify(csv, record, { allowAddedColumns });
|
|
125
|
+
const mode = allowAddedColumns ? " (column-scoped)" : "";
|
|
126
|
+
if (result.ok) {
|
|
127
|
+
process.stdout.write(`safeseed verify${mode}: OK — ${result.checked.rows} rows, ${result.checked.fields} fields in range\n`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
process.stdout.write(`safeseed verify${mode}: FAIL — ${result.failures.length} issue(s)\n`);
|
|
131
|
+
for (const f of result.failures.slice(0, 50)) {
|
|
132
|
+
process.stdout.write(` [${f.kind}] ${f.message}\n`);
|
|
133
|
+
}
|
|
134
|
+
if (result.failures.length > 50) {
|
|
135
|
+
process.stdout.write(` ...and ${result.failures.length - 50} more\n`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (result.unattestedColumns.length > 0) {
|
|
139
|
+
process.stdout.write(` unattested (added) columns, NOT vouched for — scan these: ${result.unattestedColumns.join(", ")}\n`);
|
|
140
|
+
}
|
|
141
|
+
for (const w of result.warnings) {
|
|
142
|
+
process.stdout.write(` warning: ${w}\n`);
|
|
143
|
+
}
|
|
144
|
+
process.exit(exitCode(result));
|
|
145
|
+
}
|
|
146
|
+
function cmdScan(p) {
|
|
147
|
+
let columns = [];
|
|
148
|
+
if (typeof p.flags.config === "string") {
|
|
149
|
+
const cfg = JSON.parse(readFileSync(p.flags.config, "utf8"));
|
|
150
|
+
columns = cfg.columns ?? cfg.schema ?? [];
|
|
151
|
+
}
|
|
152
|
+
if (typeof p.flags.fields === "string")
|
|
153
|
+
columns = parseFields(p.flags.fields);
|
|
154
|
+
if (columns.length === 0)
|
|
155
|
+
fail("no columns (use --config <file> or --fields name:type,...)");
|
|
156
|
+
const csv = readFileSync(reqStr(p, "in"), "utf8");
|
|
157
|
+
const result = scan({ csv, columns });
|
|
158
|
+
if (result.ok) {
|
|
159
|
+
process.stdout.write(`safeseed scan: clean — ${result.scannedRows} rows, no candidate PII\n`);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
process.stdout.write(`safeseed scan: ${result.findings.length} candidate(s) across ${result.scannedRows} rows\n`);
|
|
163
|
+
for (const f of result.findings.slice(0, 50)) {
|
|
164
|
+
process.stdout.write(` row ${f.row} ${f.field}: "${f.value}"\n`);
|
|
165
|
+
}
|
|
166
|
+
if (result.findings.length > 50) {
|
|
167
|
+
process.stdout.write(` ...and ${result.findings.length - 50} more\n`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
process.exit(result.ok ? 0 : 1);
|
|
171
|
+
}
|
|
172
|
+
function cmdCatalog() {
|
|
173
|
+
process.stdout.write(JSON.stringify({ version: CATALOG_VERSION, entries: CATALOG }, null, 2) + "\n");
|
|
174
|
+
}
|
|
175
|
+
function printUsage() {
|
|
176
|
+
process.stdout.write([
|
|
177
|
+
`safeseed ${SAFESEED_VERSION} — confirmably-synthetic test data by construction`,
|
|
178
|
+
"",
|
|
179
|
+
"Usage:",
|
|
180
|
+
" safeseed generate --fields <name:type,...> --rows N --seed S [--out f.csv] [--record r.json] [--format-valid true|false]",
|
|
181
|
+
" safeseed generate --config gen.json [--out f.csv] [--record r.json]",
|
|
182
|
+
" safeseed verify --in f.csv --record r.json [--allow-added-columns]",
|
|
183
|
+
" safeseed scan --in f.csv --fields <name:type,...>",
|
|
184
|
+
" safeseed catalog",
|
|
185
|
+
" safeseed version",
|
|
186
|
+
"",
|
|
187
|
+
"Field types: " + [...VALID_TYPES].join(", "),
|
|
188
|
+
"Exit codes: 0 clean · 1 drift/findings · 2 usage/IO error",
|
|
189
|
+
].join("\n") + "\n");
|
|
190
|
+
}
|
|
191
|
+
async function main() {
|
|
192
|
+
const p = parseArgs(process.argv.slice(2));
|
|
193
|
+
const cmd = p._[0];
|
|
194
|
+
switch (cmd) {
|
|
195
|
+
case "generate":
|
|
196
|
+
await cmdGenerate(p);
|
|
197
|
+
break;
|
|
198
|
+
case "verify":
|
|
199
|
+
await cmdVerify(p);
|
|
200
|
+
break;
|
|
201
|
+
case "scan":
|
|
202
|
+
cmdScan(p);
|
|
203
|
+
break;
|
|
204
|
+
case "catalog":
|
|
205
|
+
cmdCatalog();
|
|
206
|
+
break;
|
|
207
|
+
case "version":
|
|
208
|
+
case undefined:
|
|
209
|
+
if (cmd === "version") {
|
|
210
|
+
process.stdout.write(`safeseed ${SAFESEED_VERSION} (catalog ${CATALOG_VERSION})\n`);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
printUsage();
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
case "help":
|
|
217
|
+
printUsage();
|
|
218
|
+
break;
|
|
219
|
+
default:
|
|
220
|
+
printUsage();
|
|
221
|
+
process.exit(2);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
main().catch((e) => {
|
|
225
|
+
process.stderr.write(`safeseed: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
226
|
+
process.exit(2);
|
|
227
|
+
});
|
|
228
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EACL,QAAQ,EACR,KAAK,EACL,aAAa,EACb,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,eAAe,EACf,gBAAgB,GAKjB,MAAM,YAAY,CAAC;AAOpB,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,KAAK,GAAqC,EAAE,CAAC;IACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACnB,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC1B,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBACZ,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBACvB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACzB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACjD,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;oBAClB,CAAC,EAAE,CAAC;gBACN,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;gBACpB,CAAC;YACH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AAClC,CAAC;AAED,SAAS,IAAI,CAAC,GAAW;IACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,MAAM,CAAC,CAAS,EAAE,GAAW;IACpC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE;QAAE,IAAI,CAAC,sBAAsB,GAAG,EAAE,CAAC,CAAC;IACzE,OAAO,CAAW,CAAC;AACrB,CAAC;AAED,MAAM,WAAW,GAAG,IAAI,GAAG,CAAS,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAEjE,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAClC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI;YAAE,IAAI,CAAC,uBAAuB,IAAI,wBAAwB,CAAC,CAAC;QAC9E,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,IAAI,CAAC,uBAAuB,IAAI,2BAA2B,CAAC,CAAC;QACzF,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAiB,EAAE,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,CAAS;IAC5B,IAAI,MAAM,GAAkB,EAAE,CAAC;IAC/B,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,WAAW,GAAG,IAAI,CAAC;IAEvB,IAAI,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAKzD,CAAC;QACH,IAAI,GAAG,CAAC,MAAM;YAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QACpC,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QAClD,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QAClD,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,SAAS;YAAE,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;IAC1E,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ;QAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7E,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5D,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5D,IAAI,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,SAAS;QAAE,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,OAAO,CAAC;IAE7F,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,IAAI,CAAC,2DAA2D,CAAC,CAAC;IAC3F,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;QAAE,IAAI,CAAC,mCAAmC,CAAC,CAAC;IACpF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAE5D,MAAM,EAAE,GAAG,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAEvC,IAAI,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,QAAQ;QAAE,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;;QAChE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE/B,MAAM,UAAU,GACd,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ;QAChC,CAAC,CAAC,aAAa,CACX,EAAE,EACF,GAAG,EACH,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAC3E,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YACb,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,MAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC/E,CAAC,CAAC;QACJ,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAExB,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE;QAC1B,IAAI,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;YACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC,IAAI,CAAC,MAAM,YAAY,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;QACrF,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAW;IACtC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvB,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,MAAM,CAAC;AACpC,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,CAAS;IAChC,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAc,CAAC;IAClF,MAAM,iBAAiB,GAAG,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,iBAAiB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kBAAkB,IAAI,UAAU,MAAM,CAAC,OAAO,CAAC,IAAI,UAAU,MAAM,CAAC,OAAO,CAAC,MAAM,oBAAoB,CACvG,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,IAAI,YAAY,MAAM,CAAC,QAAQ,CAAC,MAAM,aAAa,CAAC,CAAC;QAC5F,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,EAAE,SAAS,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,+DAA+D,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CACvG,CAAC;IACJ,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,OAAO,CAAC,CAAS;IACxB,IAAI,OAAO,GAAiB,EAAE,CAAC;IAC/B,IAAI,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAGzD,CAAC;QACH,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;IAC5C,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,IAAI,CAAC,4DAA4D,CAAC,CAAC;IAE7F,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,MAAM,CAAC,WAAW,2BAA2B,CAAC,CAAC;IAChG,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kBAAkB,MAAM,CAAC,QAAQ,CAAC,MAAM,wBAAwB,MAAM,CAAC,WAAW,SAAS,CAC5F,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,EAAE,SAAS,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACvG,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB;QACE,YAAY,gBAAgB,oDAAoD;QAChF,EAAE;QACF,QAAQ;QACR,4HAA4H;QAC5H,uEAAuE;QACvE,wEAAwE;QACxE,yDAAyD;QACzD,oBAAoB;QACpB,oBAAoB;QACpB,EAAE;QACF,eAAe,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC7C,2DAA2D;KAC5D,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CACpB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnB,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,UAAU;YACb,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC;YACrB,MAAM;QACR,KAAK,QAAQ;YACX,MAAM,SAAS,CAAC,CAAC,CAAC,CAAC;YACnB,MAAM;QACR,KAAK,MAAM;YACT,OAAO,CAAC,CAAC,CAAC,CAAC;YACX,MAAM;QACR,KAAK,SAAS;YACZ,UAAU,EAAE,CAAC;YACb,MAAM;QACR,KAAK,SAAS,CAAC;QACf,KAAK,SAAS;YACZ,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,gBAAgB,aAAa,eAAe,KAAK,CAAC,CAAC;YACtF,CAAC;iBAAM,CAAC;gBACN,UAAU,EAAE,CAAC;YACf,CAAC;YACD,MAAM;QACR,KAAK,MAAM;YACT,UAAU,EAAE,CAAC;YACb,MAAM;QACR;YACE,UAAU,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;IAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAClF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/csv.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal, correct CSV (RFC 4180-ish) serializer and parser. Kept in-house so the
|
|
3
|
+
* core has no runtime dependency to audit — the whole pitch is "audit a few
|
|
4
|
+
* hundred cited lines once, trust every output."
|
|
5
|
+
*/
|
|
6
|
+
/** Serialize columns + rows to a CSV string with a trailing newline. */
|
|
7
|
+
export declare function toCsv(columns: readonly string[], rows: readonly (readonly string[])[]): string;
|
|
8
|
+
/**
|
|
9
|
+
* Canonical serialization of one column's cell values, used for the per-column
|
|
10
|
+
* hash in a run record. JSON encoding is unambiguous — a comma, quote, or newline
|
|
11
|
+
* inside a cell can't collide with the delimiter — so two columns hash equal iff
|
|
12
|
+
* their ordered values are identical. Generation and column-scoped verify both go
|
|
13
|
+
* through this one function, so they can never disagree on what a column "is".
|
|
14
|
+
*/
|
|
15
|
+
export declare function canonicalColumn(values: readonly string[]): string;
|
|
16
|
+
/** Parse a CSV string into header columns and data rows. */
|
|
17
|
+
export declare function parseCsv(text: string): {
|
|
18
|
+
columns: string[];
|
|
19
|
+
rows: string[][];
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=csv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wEAAwE;AACxE,wBAAgB,KAAK,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,GAAG,MAAM,CAO9F;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAEjE;AAED,4DAA4D;AAC5D,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAA;CAAE,CAuD9E"}
|
package/dist/csv.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal, correct CSV (RFC 4180-ish) serializer and parser. Kept in-house so the
|
|
3
|
+
* core has no runtime dependency to audit — the whole pitch is "audit a few
|
|
4
|
+
* hundred cited lines once, trust every output."
|
|
5
|
+
*/
|
|
6
|
+
/** Serialize columns + rows to a CSV string with a trailing newline. */
|
|
7
|
+
export function toCsv(columns, rows) {
|
|
8
|
+
const escape = (v) => /[",\r\n]/.test(v) ? `"${v.replace(/"/g, '""')}"` : v;
|
|
9
|
+
const header = columns.map(escape).join(",");
|
|
10
|
+
if (rows.length === 0)
|
|
11
|
+
return `${header}\n`;
|
|
12
|
+
const body = rows.map((r) => r.map(escape).join(",")).join("\n");
|
|
13
|
+
return `${header}\n${body}\n`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Canonical serialization of one column's cell values, used for the per-column
|
|
17
|
+
* hash in a run record. JSON encoding is unambiguous — a comma, quote, or newline
|
|
18
|
+
* inside a cell can't collide with the delimiter — so two columns hash equal iff
|
|
19
|
+
* their ordered values are identical. Generation and column-scoped verify both go
|
|
20
|
+
* through this one function, so they can never disagree on what a column "is".
|
|
21
|
+
*/
|
|
22
|
+
export function canonicalColumn(values) {
|
|
23
|
+
return JSON.stringify(values);
|
|
24
|
+
}
|
|
25
|
+
/** Parse a CSV string into header columns and data rows. */
|
|
26
|
+
export function parseCsv(text) {
|
|
27
|
+
const records = [];
|
|
28
|
+
let field = "";
|
|
29
|
+
let record = [];
|
|
30
|
+
let inQuotes = false;
|
|
31
|
+
let started = false; // have we seen any content for the current record/field?
|
|
32
|
+
const pushField = () => {
|
|
33
|
+
record.push(field);
|
|
34
|
+
field = "";
|
|
35
|
+
};
|
|
36
|
+
const pushRecord = () => {
|
|
37
|
+
pushField();
|
|
38
|
+
records.push(record);
|
|
39
|
+
record = [];
|
|
40
|
+
started = false;
|
|
41
|
+
};
|
|
42
|
+
for (let i = 0; i < text.length; i++) {
|
|
43
|
+
const c = text[i];
|
|
44
|
+
if (inQuotes) {
|
|
45
|
+
if (c === '"') {
|
|
46
|
+
if (text[i + 1] === '"') {
|
|
47
|
+
field += '"';
|
|
48
|
+
i++;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
inQuotes = false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
field += c;
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (c === '"') {
|
|
60
|
+
inQuotes = true;
|
|
61
|
+
started = true;
|
|
62
|
+
}
|
|
63
|
+
else if (c === ",") {
|
|
64
|
+
pushField();
|
|
65
|
+
started = true;
|
|
66
|
+
}
|
|
67
|
+
else if (c === "\n") {
|
|
68
|
+
pushRecord();
|
|
69
|
+
}
|
|
70
|
+
else if (c === "\r") {
|
|
71
|
+
// swallow; newline handled on \n
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
field += c;
|
|
75
|
+
started = true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Flush a final record only if the file did not end on a clean record break.
|
|
79
|
+
if (started || field.length > 0 || record.length > 0) {
|
|
80
|
+
pushRecord();
|
|
81
|
+
}
|
|
82
|
+
const columns = records.shift() ?? [];
|
|
83
|
+
return { columns, rows: records };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=csv.js.map
|
package/dist/csv.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.js","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FieldType } from "./types.js";
|
|
2
|
+
export interface FieldSchema {
|
|
3
|
+
/** Output column name. */
|
|
4
|
+
name: string;
|
|
5
|
+
/** Catalog field type that governs the reserved range. */
|
|
6
|
+
type: FieldType;
|
|
7
|
+
}
|
|
8
|
+
export interface GenerateOptions {
|
|
9
|
+
schema: FieldSchema[];
|
|
10
|
+
rows: number;
|
|
11
|
+
/** Seed for deterministic output. Same seed + schema => identical dataset. */
|
|
12
|
+
seed: number;
|
|
13
|
+
/**
|
|
14
|
+
* Format-valid mode (default true): render values that pass common validators
|
|
15
|
+
* while staying in range (10-digit phones, NNN-NN-NNNN SSNs). When false,
|
|
16
|
+
* values are rendered in a looser, even-more-obviously-test form.
|
|
17
|
+
*/
|
|
18
|
+
formatValid?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface GeneratedDataset {
|
|
21
|
+
columns: string[];
|
|
22
|
+
rows: string[][];
|
|
23
|
+
schema: FieldSchema[];
|
|
24
|
+
seed: number;
|
|
25
|
+
catalogVersion: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function generate(opts: GenerateOptions): GeneratedDataset;
|
|
28
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,WAAW,WAAW;IAC1B,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IACjB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;CACxB;AA2ED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,gBAAgB,CAmBhE"}
|
package/dist/generate.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema-driven, deterministic generation of safe-by-construction test data.
|
|
3
|
+
*
|
|
4
|
+
* Every PII-shaped value is drawn from the cited reserved ranges in `catalog.ts`;
|
|
5
|
+
* structurally-fake fields (names, addresses) are emitted as self-evidently fake
|
|
6
|
+
* tokens. Output is a function of (schema, seed), so a generated dataset is a
|
|
7
|
+
* committable, reviewable fixture.
|
|
8
|
+
*/
|
|
9
|
+
import { CATALOG_VERSION } from "./catalog.js";
|
|
10
|
+
import { mulberry32, pick, intBetween } from "./rng.js";
|
|
11
|
+
const EMAIL_DOMAINS = ["example.com", "example.net", "example.org"];
|
|
12
|
+
const IPV4_BLOCKS = [
|
|
13
|
+
[192, 0, 2],
|
|
14
|
+
[198, 51, 100],
|
|
15
|
+
[203, 0, 113],
|
|
16
|
+
];
|
|
17
|
+
const SSN_INVALID_AREAS = ["900", "901", "902", "910", "987", "999", "000", "666"];
|
|
18
|
+
const STREET_SUFFIX = ["Way", "St", "Ave", "Rd", "Blvd"];
|
|
19
|
+
const CARD_TEST_NUMBERS = [
|
|
20
|
+
"4242424242424242",
|
|
21
|
+
"4111111111111111",
|
|
22
|
+
"4000056655665556",
|
|
23
|
+
"5555555555554444",
|
|
24
|
+
"5105105105105100",
|
|
25
|
+
"2223003122003222",
|
|
26
|
+
"378282246310005",
|
|
27
|
+
"371449635398431",
|
|
28
|
+
"6011111111111117",
|
|
29
|
+
"3530111333300000",
|
|
30
|
+
];
|
|
31
|
+
function pad(n, width) {
|
|
32
|
+
return String(n).padStart(width, "0");
|
|
33
|
+
}
|
|
34
|
+
function generateValue(type, rng, row, formatValid) {
|
|
35
|
+
switch (type) {
|
|
36
|
+
case "email":
|
|
37
|
+
return formatValid
|
|
38
|
+
? `user${row}@${pick(rng, EMAIL_DOMAINS)}`
|
|
39
|
+
: `test_${pad(row, 6)}@example.invalid`;
|
|
40
|
+
case "domain":
|
|
41
|
+
return `host${row}.invalid`;
|
|
42
|
+
case "ipv4": {
|
|
43
|
+
const block = pick(rng, IPV4_BLOCKS);
|
|
44
|
+
return `${block[0]}.${block[1]}.${block[2]}.${intBetween(rng, 1, 254)}`;
|
|
45
|
+
}
|
|
46
|
+
case "ipv6": {
|
|
47
|
+
// Split the counter across two hextets so it never overflows a single hextet
|
|
48
|
+
// (>0xffff) and always stays inside the RFC 3849 2001:db8::/32 documentation prefix.
|
|
49
|
+
const hi = (row >>> 16) & 0xffff;
|
|
50
|
+
const lo = row & 0xffff;
|
|
51
|
+
return hi ? `2001:db8::${hi.toString(16)}:${lo.toString(16)}` : `2001:db8::${lo.toString(16)}`;
|
|
52
|
+
}
|
|
53
|
+
case "phone": {
|
|
54
|
+
const line = pad(intBetween(rng, 100, 199), 4);
|
|
55
|
+
if (formatValid) {
|
|
56
|
+
const npa = intBetween(rng, 200, 989);
|
|
57
|
+
return `(${npa}) 555-${line}`;
|
|
58
|
+
}
|
|
59
|
+
return `555-${line}`;
|
|
60
|
+
}
|
|
61
|
+
case "ssn": {
|
|
62
|
+
const area = pick(rng, SSN_INVALID_AREAS);
|
|
63
|
+
const group = pad(intBetween(rng, 1, 99), 2);
|
|
64
|
+
const serial = pad(intBetween(rng, 1, 9999), 4);
|
|
65
|
+
return `${area}-${group}-${serial}`;
|
|
66
|
+
}
|
|
67
|
+
case "creditCard":
|
|
68
|
+
return pick(rng, CARD_TEST_NUMBERS);
|
|
69
|
+
case "firstName":
|
|
70
|
+
return `TEST_Firstname_${pad(row, 6)}`;
|
|
71
|
+
case "lastName":
|
|
72
|
+
return `TEST_Lastname_${pad(row, 6)}`;
|
|
73
|
+
case "fullName":
|
|
74
|
+
return `TEST_Person_${pad(row, 6)}`;
|
|
75
|
+
case "streetAddress":
|
|
76
|
+
return `${row} Example ${pick(rng, STREET_SUFFIX)}`;
|
|
77
|
+
case "freeText":
|
|
78
|
+
return `TEST_Text_${pad(row, 6)}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function generate(opts) {
|
|
82
|
+
const formatValid = opts.formatValid ?? true;
|
|
83
|
+
const rng = mulberry32(opts.seed);
|
|
84
|
+
const columns = opts.schema.map((f) => f.name);
|
|
85
|
+
const rows = [];
|
|
86
|
+
for (let i = 0; i < opts.rows; i++) {
|
|
87
|
+
const row = [];
|
|
88
|
+
for (const field of opts.schema) {
|
|
89
|
+
row.push(generateValue(field.type, rng, i + 1, formatValid));
|
|
90
|
+
}
|
|
91
|
+
rows.push(row);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
columns,
|
|
95
|
+
rows,
|
|
96
|
+
schema: opts.schema,
|
|
97
|
+
seed: opts.seed,
|
|
98
|
+
catalogVersion: CATALOG_VERSION,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"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"}
|
package/dist/hash.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isomorphic SHA-256 over the Web Crypto API.
|
|
3
|
+
*
|
|
4
|
+
* `crypto.subtle` is provided by the platform in both modern Node (>=18) and
|
|
5
|
+
* browsers, so SafeSeed needs no hashing dependency to bundle or audit. The hash
|
|
6
|
+
* is used for the *tamper-evident run record* — it proves a file is byte-for-byte
|
|
7
|
+
* the one that was generated, not that the file is free of personal data.
|
|
8
|
+
*/
|
|
9
|
+
export declare function sha256Hex(input: string): Promise<string>;
|
|
10
|
+
//# sourceMappingURL=hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9D"}
|
package/dist/hash.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isomorphic SHA-256 over the Web Crypto API.
|
|
3
|
+
*
|
|
4
|
+
* `crypto.subtle` is provided by the platform in both modern Node (>=18) and
|
|
5
|
+
* browsers, so SafeSeed needs no hashing dependency to bundle or audit. The hash
|
|
6
|
+
* is used for the *tamper-evident run record* — it proves a file is byte-for-byte
|
|
7
|
+
* the one that was generated, not that the file is free of personal data.
|
|
8
|
+
*/
|
|
9
|
+
export async function sha256Hex(input) {
|
|
10
|
+
const bytes = new TextEncoder().encode(input);
|
|
11
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
12
|
+
return Array.from(new Uint8Array(digest))
|
|
13
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
14
|
+
.join("");
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=hash.js.map
|
package/dist/hash.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAa;IAC3C,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC5D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;SACtC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SafeSeed — confirmably-synthetic test data by construction.
|
|
3
|
+
*
|
|
4
|
+
* Generate safe-by-construction test data from standards-reserved ranges, bind a
|
|
5
|
+
* tamper-evident run record to it, verify a file stays in range, and scan existing
|
|
6
|
+
* data for leaked real PII. Pure TypeScript, zero runtime dependencies, isomorphic
|
|
7
|
+
* (Node >=18 and browsers).
|
|
8
|
+
*/
|
|
9
|
+
export type { Tier, FieldType } from "./types.js";
|
|
10
|
+
export { CATALOG, CATALOG_VERSION, getEntry, isReserved, isSelfEvidentlyFake, type CatalogEntry, type ReservedSpec, } from "./catalog.js";
|
|
11
|
+
export { generate, type FieldSchema, type GenerateOptions, type GeneratedDataset, } from "./generate.js";
|
|
12
|
+
export { makeRunRecord, ATTESTATION, SAFESEED_VERSION, type RunRecord, type FieldRecord, } from "./record.js";
|
|
13
|
+
export { verify, exitCode, type VerifyResult, type VerifyFailure, type VerifyFailureKind, type VerifyOptions, } from "./verify.js";
|
|
14
|
+
export { scan, type ScanColumn, type ScanOptions, type ScanResult, type ScanFinding, } from "./scan.js";
|
|
15
|
+
export { toCsv, parseCsv, canonicalColumn } from "./csv.js";
|
|
16
|
+
export { sha256Hex } from "./hash.js";
|
|
17
|
+
export { luhnValid } from "./luhn.js";
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,EACL,OAAO,EACP,eAAe,EACf,QAAQ,EACR,UAAU,EACV,mBAAmB,EACnB,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,QAAQ,EACR,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,KAAK,SAAS,EACd,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,MAAM,EACN,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,IAAI,EACJ,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { CATALOG, CATALOG_VERSION, getEntry, isReserved, isSelfEvidentlyFake, } from "./catalog.js";
|
|
2
|
+
export { generate, } from "./generate.js";
|
|
3
|
+
export { makeRunRecord, ATTESTATION, SAFESEED_VERSION, } from "./record.js";
|
|
4
|
+
export { verify, exitCode, } from "./verify.js";
|
|
5
|
+
export { scan, } from "./scan.js";
|
|
6
|
+
export { toCsv, parseCsv, canonicalColumn } from "./csv.js";
|
|
7
|
+
export { sha256Hex } from "./hash.js";
|
|
8
|
+
export { luhnValid } from "./luhn.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,OAAO,EACP,eAAe,EACf,QAAQ,EACR,UAAU,EACV,mBAAmB,GAGpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,QAAQ,GAIT,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,aAAa,EACb,WAAW,EACX,gBAAgB,GAGjB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,MAAM,EACN,QAAQ,GAKT,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,IAAI,GAKL,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/luhn.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Luhn checksum. Used only to confirm that designated card *test* numbers pass
|
|
3
|
+
* the same validation a real card would — which is exactly why they sit in the
|
|
4
|
+
* `designated-test-only` tier and not `provably-non-real`: passing Luhn is the
|
|
5
|
+
* point, so the value is valid-looking, not impossible.
|
|
6
|
+
*/
|
|
7
|
+
export declare function luhnValid(digits: string): boolean;
|
|
8
|
+
//# sourceMappingURL=luhn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"luhn.d.ts","sourceRoot":"","sources":["../src/luhn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAcjD"}
|
package/dist/luhn.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Luhn checksum. Used only to confirm that designated card *test* numbers pass
|
|
3
|
+
* the same validation a real card would — which is exactly why they sit in the
|
|
4
|
+
* `designated-test-only` tier and not `provably-non-real`: passing Luhn is the
|
|
5
|
+
* point, so the value is valid-looking, not impossible.
|
|
6
|
+
*/
|
|
7
|
+
export function luhnValid(digits) {
|
|
8
|
+
if (!/^\d+$/.test(digits))
|
|
9
|
+
return false;
|
|
10
|
+
let sum = 0;
|
|
11
|
+
let double = false;
|
|
12
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
13
|
+
let d = digits.charCodeAt(i) - 48;
|
|
14
|
+
if (double) {
|
|
15
|
+
d *= 2;
|
|
16
|
+
if (d > 9)
|
|
17
|
+
d -= 9;
|
|
18
|
+
}
|
|
19
|
+
sum += d;
|
|
20
|
+
double = !double;
|
|
21
|
+
}
|
|
22
|
+
return sum % 10 === 0;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=luhn.js.map
|
package/dist/luhn.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"luhn.js","sourceRoot":"","sources":["../src/luhn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,IAAI,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;QAClC,IAAI,MAAM,EAAE,CAAC;YACX,CAAC,IAAI,CAAC,CAAC;YACP,IAAI,CAAC,GAAG,CAAC;gBAAE,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;QACD,GAAG,IAAI,CAAC,CAAC;QACT,MAAM,GAAG,CAAC,MAAM,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC;AACxB,CAAC"}
|
package/dist/net.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IP address membership helpers, so the catalog can say "this value is inside a
|
|
3
|
+
* standards-reserved block" precisely rather than by string prefix.
|
|
4
|
+
*/
|
|
5
|
+
/** Parse a dotted-quad IPv4 string to an unsigned 32-bit int, or null if invalid. */
|
|
6
|
+
export declare function ipv4ToInt(ip: string): number | null;
|
|
7
|
+
/** True if `ip` falls inside the given IPv4 CIDR block (e.g. "192.0.2.0/24"). */
|
|
8
|
+
export declare function ipv4InCidr(ip: string, cidr: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Expand an IPv6 string to its 8 hextets (numbers), handling a single "::"
|
|
11
|
+
* abbreviation and an optional zone id. Returns null if malformed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function expandIpv6(ip: string): number[] | null;
|
|
14
|
+
/** True if `ip` falls inside the given IPv6 prefix (e.g. "2001:db8::/32"). */
|
|
15
|
+
export declare function ipv6InPrefix(ip: string, prefixCidr: string): boolean;
|
|
16
|
+
//# sourceMappingURL=net.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"net.d.ts","sourceRoot":"","sources":["../src/net.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qFAAqF;AACrF,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWnD;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAS5D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAgCtD;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAepE"}
|