@cosyte/x12 0.0.6 → 0.0.8
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/CHANGELOG.md +129 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -42,8 +42,87 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
42
42
|
a warning `message` is a frozen-registry lookup with positional metadata only, but an orphan is
|
|
43
43
|
document content, verbatim, exactly like `tx.rawSegments`. Log `context` and `segmentIndex`.
|
|
44
44
|
|
|
45
|
+
### Security
|
|
46
|
+
|
|
47
|
+
- **The PHI scanner refuses an in-scope entry that is not a regular file, on both of its enumerating
|
|
48
|
+
routes.** A symbolic link under a scan root pointing at a PHI-bearing file used to scan CLEAN on
|
|
49
|
+
both, so the pre-commit gate and CI both reported "no hits" over a capture the scan never read.
|
|
50
|
+
Measured on `5779542`, against a throwaway repository laid out like this one, using a synthetic
|
|
51
|
+
`.edi` payload whose NM1 person name, DMG date of birth, PER phone and `REF*SY` SSN are all hits at
|
|
52
|
+
exit 1 when the same bytes sit at a regular file:
|
|
53
|
+
- the all-mode walk enumerates `Dirent.isFile()`, which is an lstat answer, so a link is neither a
|
|
54
|
+
file nor a directory and fell out of the loop silently. A link under `test/fixtures`, a link
|
|
55
|
+
under `src/`, and a linked DIRECTORY (which takes a whole subtree with it) each reported
|
|
56
|
+
`OK - no hits` at exit 0;
|
|
57
|
+
- `--staged` reads content with `git show :<path>`, and git stores a link as its TARGET PATH under
|
|
58
|
+
mode `120000`, so that route was handed the path text and never the target's bytes. A staged
|
|
59
|
+
link reported `OK - no hits` at exit 0.
|
|
60
|
+
|
|
61
|
+
Both routes now refuse the scan (exit 2, the existing "could not complete" code) and name every
|
|
62
|
+
offender, not just the first. Neither route is made to FOLLOW an ENTRY it enumerated: following
|
|
63
|
+
would read bytes the enumeration does not control, and git does not carry those bytes anyway, so a
|
|
64
|
+
hit on them would be a claim about something no commit contains. That is a statement about an
|
|
65
|
+
entry, not about a scan ROOT: a walk root that is itself a link is still followed, because
|
|
66
|
+
`existsSync` and `readdirSync` both follow. Measured identically before and after this change, that
|
|
67
|
+
direction produces a superset scan rather than a blind one (the target's files are enumerated under
|
|
68
|
+
their in-root names and hit, exit 1), so it is deliberately left alone.
|
|
69
|
+
|
|
70
|
+
**A refusal names the entry's own repo-relative path and a scanner-owned token for its kind. It
|
|
71
|
+
never reports the link target**, which is working-tree text that can itself carry PHI: a target
|
|
72
|
+
path of the shape `<surname>-<given>-<dob>.edi` is the whole reason. That shape is written out
|
|
73
|
+
rather than shown, because a diagnostic about a PHI leak is itself a PHI surface. The concern was
|
|
74
|
+
not hypothetical here: measured at base, a staged link whose target name was a dashed-SSN shape
|
|
75
|
+
exited 1 and printed that shape, because `git show` handed the path text straight to the
|
|
76
|
+
cross-cutting shape pass.
|
|
77
|
+
|
|
78
|
+
**The `--staged` filter is now `AMT`, and `T` is the one-letter difference that made the mode check
|
|
79
|
+
reachable.** Replacing a TRACKED regular file with a link is neither an add nor a modify: measured
|
|
80
|
+
on this tree, `git diff --cached --raw --diff-filter=AM` returned zero rows for that change while
|
|
81
|
+
the unfiltered `--raw` showed `:100644 120000 <sha> <sha> T`. Without `T` the record died before
|
|
82
|
+
any mode could be read and the hook passed a mode-`120000` blob green. Admitting `T` also covers
|
|
83
|
+
the reverse typechange, a link replaced by a real file bearing PHI. The route reads
|
|
84
|
+
`git diff --cached --raw -z` rather than `--name-only` because the destination mode is the only
|
|
85
|
+
thing that distinguishes a staged regular file from a staged link or gitlink, and
|
|
86
|
+
`git show :<path>` answers all three without complaint. A record that does not parse refuses rather
|
|
87
|
+
than being skipped.
|
|
88
|
+
|
|
89
|
+
**Each route keeps its own existing boundary**: the walk still excludes a gitignored entry (the same
|
|
90
|
+
rule that already excludes a gitignored file), and `--staged` still looks only at
|
|
91
|
+
`test/fixtures/**` and `src/**.ts`. This narrows what those scopes admit; it does not widen them.
|
|
92
|
+
A gitlink under a scanned prefix already exited 2 before this change, but by `git show` failing and
|
|
93
|
+
echoing git's own text; it is now refused at enumeration and named by kind.
|
|
94
|
+
|
|
95
|
+
**`paths` mode is deliberately unchanged, because it was never blind**: it reads with
|
|
96
|
+
`readFileSync`, which follows a link, so an explicitly named path that is a link to a PHI-bearing
|
|
97
|
+
file is scanned and hits (measured, exit 1).
|
|
98
|
+
|
|
99
|
+
**Not closed here, and stated rather than implied**: `R` (rename) and `C` (copy) are still not
|
|
100
|
+
enumerated by `--staged` at all, which is pre-existing and needs the two-path `--raw` record shape
|
|
101
|
+
handled. Measured, so the cost is not left to inference: renaming a fixture while substituting a
|
|
102
|
+
real name stages as `:100644 100644 <sha> <sha> R080 <old> <new>`, which both `AM` and `AMT` return
|
|
103
|
+
zero rows for, and `--staged` exits 0 over a payload that is a hit as an ordinary add; `git mv` of
|
|
104
|
+
an already-committed link into `test/fixtures/` is `R100` and is likewise not refused. The all-mode
|
|
105
|
+
sweep is the backstop for both (exit 1 and exit 2 respectively), so the gap is at pre-commit, not
|
|
106
|
+
in CI. Also unclosed: a scan that observed nothing is still reported clean rather than refused; and
|
|
107
|
+
the
|
|
108
|
+
enumerate-then-read window in all mode is untouched, because tolerating a failed read pulls the
|
|
109
|
+
opposite way from narrowing what the enumeration admits and belongs in its own change. No library
|
|
110
|
+
code changed and no published type changed.
|
|
111
|
+
|
|
45
112
|
### Fixed
|
|
46
113
|
|
|
114
|
+
- **The `attw` publish gate no longer passes a tarball that carries no type declarations.** The
|
|
115
|
+
`attw` script was the bare CLI, and `@arethetypeswrong/cli` returns 0 whenever its analysis found
|
|
116
|
+
no types at all, before it reads the problem list. For a package that ships types, "This package
|
|
117
|
+
does not contain types." means the declarations were missing from the tarball, which is a broken
|
|
118
|
+
publish that the gate reported as a pass. Reproduced against this package with no concurrency
|
|
119
|
+
involved: with `dist/` removed, and with only `dist/index.d.ts` and `dist/index.d.cts` removed,
|
|
120
|
+
the CLI printed that sentence and exited 0 in both cases. The second is a state every build passes
|
|
121
|
+
through, because `tsup` writes the JS before the declarations (measured at 1.92 seconds apart on
|
|
122
|
+
one clean build here). `pnpm attw` now runs `scripts/attw.mjs`, which checks that every relative
|
|
123
|
+
path `package.json` promises exists and is non-empty before invoking the CLI, and fails afterwards
|
|
124
|
+
if the CLI still reports an untyped package. No change to the library or to any published type.
|
|
125
|
+
|
|
47
126
|
- **A `defineProfile()` refusal message no longer grows with the value you passed in.** Twelve
|
|
48
127
|
refusal sites in `src/profiles/validate.ts` hold twenty-three caller-value holes between them, and
|
|
49
128
|
every one now routes through `renderCallerValue` or `renderCallerJson`. Re-derived on this tree
|
|
@@ -286,6 +365,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
286
365
|
|
|
287
366
|
### Tests
|
|
288
367
|
|
|
368
|
+
- **The PHI-scanner suite stopped paying 30 of its 32 `tsx` start-ups, and the global `testTimeout`
|
|
369
|
+
now says what it does and does not cover.** No library code, no public surface and no timeout value
|
|
370
|
+
changed; `scripts/phi-scan.ts` and `scripts/attw.mjs` are untouched.
|
|
371
|
+
|
|
372
|
+
Nearly every case in `test/scripts/phi-scan.test.ts` spawns the scanner. Counted at runtime on
|
|
373
|
+
both trees with a `spawnSync` shim: **32 spawns across 32 cases, all under `tsx`**, becoming
|
|
374
|
+
**36 spawns across 33 cases, 34 under `node` and 2 under `tsx`**. So 30 `tsx` start-ups were
|
|
375
|
+
removed and 2 were kept deliberately, in the equivalence case below. The scanner is type-annotated
|
|
376
|
+
Node that needs erasing and nothing more, and Node 22.18 or newer strips types itself, so the
|
|
377
|
+
spawns now use `process.execPath`. Measured on a 12-CPU cgroup quota with
|
|
378
|
+
`availableParallelism()` 12 and other workers running (load average 8.9 to 11.3, a realistic
|
|
379
|
+
condition rather than a quiet one): one scanner start is a 441 ms median under `tsx` against
|
|
380
|
+
149 ms under `node`, seven runs each. Interleaved BASE/HEAD under `pnpm test:coverage`, two rounds
|
|
381
|
+
each so the arms share a load condition, that file went 17.2 s / 17.5 s to 8.6 s / 8.6 s, and
|
|
382
|
+
15.7 s to 6.6 s run on its own. Total CPU across all workers went 58.9 s / 58.4 s to
|
|
383
|
+
50.5 s / 49.5 s. Those medians predict 8.2 s off this file (32 starts converted, less the 2 `node`
|
|
384
|
+
and 2 `tsx` starts the new case adds) against 9.1 s measured, so the model is the right shape and
|
|
385
|
+
about 11% light, not a match.
|
|
386
|
+
|
|
387
|
+
**The substitution is pinned as an equivalence rather than assumed.** The gate consumers actually
|
|
388
|
+
run (`pnpm phi-scan`, the pre-commit hook, CI) still invokes `tsx`, so one new case drives both
|
|
389
|
+
runners over the same violator and the same clean file and requires the same exit code, stdout and
|
|
390
|
+
stderr. It is the only place `tsx` is still spawned, and a simulated divergence reds it. **Scope it
|
|
391
|
+
honestly:** that case drives `paths` mode on one hit and one clean file, so it pins the exit-0 and
|
|
392
|
+
exit-1 verdicts and not the exit-2 refusals, nor all-mode, nor `--staged`. It is aimed at the only
|
|
393
|
+
divergence these two runners plausibly have, which is at module load, and a load-time divergence
|
|
394
|
+
cannot be confined to the routes it does not drive.
|
|
395
|
+
|
|
396
|
+
**What this did not buy, stated because the two figures diverge:** it removed about 8.6 s of CPU
|
|
397
|
+
but barely moved the suite's critical path (17.2 s / 17.5 s to 16.3 s / 16.7 s), which is now
|
|
398
|
+
`test/scripts/attw-gate.test.ts`. That file is deliberately left alone: measured, one `attw --pack`
|
|
399
|
+
on a trivial two-file package is 1,596 ms median, of which the real `npm pack` is 462 ms and the
|
|
400
|
+
rest is attw's own analysis. There is no runner to substitute there, pinning the real binary is the
|
|
401
|
+
point of that gate, and each of its cases already carries its own 60 s ceiling.
|
|
402
|
+
|
|
403
|
+
**`vitest.config.ts` now documents the scope of `testTimeout: 10_000`, which is narrower than it
|
|
404
|
+
looks in both directions.** It is a floor for ordinary tests, not where slow work gets its room:
|
|
405
|
+
the three slowest suites already take per-test ceilings, and the 10 MB+ 834 stream measured
|
|
406
|
+
8.9 s / 10.0 s / 9.3 s / 9.1 s across the four interleaved runs, so it sits AT the 10 s global on a
|
|
407
|
+
merely-loaded box (a 10.0 s reading is not evidence of which side of the bar it fell), and measured
|
|
408
|
+
24.1 s under heavier load on the same box. It is green only on its own 120 s ceiling. Raising the
|
|
409
|
+
global to
|
|
410
|
+
fit it would hand the same leash to all 1,100-odd tests and turn a genuinely hung test from broken
|
|
411
|
+
into merely slow. It is also **not a liveness net**: measured on this tree with vitest 4.1.4, an
|
|
412
|
+
async overrun reds at the ceiling, a finite synchronous overrun reds only after the work returns,
|
|
413
|
+
and an infinite synchronous loop produces no verdict at all, wedging the worker until it is killed
|
|
414
|
+
from outside (45 s, exit 143, no pass/fail line). That is the failure mode
|
|
415
|
+
`X12-CALLER-VALUE-RESIDUALS` hit for real, and the defence against it is the source scan in
|
|
416
|
+
`test/builder-array-bounds.test.ts`, not any timeout value.
|
|
417
|
+
|
|
289
418
|
- **The default emit mode's guarantees are locked against the whole committed corpus, not just the 13
|
|
290
419
|
goldens.** The goldens are already in the serializer's image, so they could only ever demonstrate
|
|
291
420
|
the easy half of the round trip. `test/serialize.test.ts` now discovers every `.edi` fixture from
|
package/dist/index.cjs
CHANGED
|
@@ -9892,7 +9892,7 @@ var profiles = Object.freeze({
|
|
|
9892
9892
|
});
|
|
9893
9893
|
|
|
9894
9894
|
// src/index.ts
|
|
9895
|
-
var VERSION = "0.0.
|
|
9895
|
+
var VERSION = "0.0.8";
|
|
9896
9896
|
|
|
9897
9897
|
exports.ACK_BUILD_ERROR_CODES = ACK_BUILD_ERROR_CODES;
|
|
9898
9898
|
exports.ALL_WARNING_MESSAGES = ALL_WARNING_MESSAGES;
|