border-customs 0.3.1 → 0.3.2
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/README.md +46 -0
- package/dist/assets/package-lock.json +4 -11
- package/dist/index.js +23 -10
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -375,6 +375,39 @@ dropped file to stderr with the sha256 of its original bytes
|
|
|
375
375
|
(`border scan: crates envelope normalized: <file> sha256=<hex>`); the rebuild regenerates
|
|
376
376
|
them from the normalized manifest, so no scanned byte is silently lost.
|
|
377
377
|
|
|
378
|
+
### Engines in consumer installs
|
|
379
|
+
|
|
380
|
+
`dist/index.js` is one self-contained esbuild bundle and every runtime asset (vendored
|
|
381
|
+
gitleaks rules, prompt template, the secretlint fingerprint snapshot) ships inside `dist/`,
|
|
382
|
+
so after `npm i -D border-customs` (or `-g`) the engine surface is:
|
|
383
|
+
|
|
384
|
+
- **publint** — installed automatically: it is an exact-pinned runtime dependency of the
|
|
385
|
+
package, invoked via bin resolution (package-local `node_modules/.bin`, the hoisted
|
|
386
|
+
project-level `node_modules/.bin`, then `PATH`, then `~/.local/bin`). The
|
|
387
|
+
`publint-fail` leg of the npm artifact stage works out of the box in a consumer install.
|
|
388
|
+
- **secretlint** — no consumer action, ever: the default lint runs **in-process** with the
|
|
389
|
+
`@secretlint/*` rule modules already declared as border's own runtime dependencies
|
|
390
|
+
(`src/engines/secretlint.ts` header). The `mode: "cli"` fallback that spawns a
|
|
391
|
+
`secretlint` binary is border-internal (test transport); it is not reachable from
|
|
392
|
+
`border.yaml` or any flag, so installing `secretlint` into your project changes nothing
|
|
393
|
+
for border. If the engine probe cannot read the bundled lock snapshot
|
|
394
|
+
(`dist/assets/package-lock.json`), the run fails closed as `DEGRADED-ENGINE` exit 2 —
|
|
395
|
+
never a silent pass.
|
|
396
|
+
- **gitleaks** — stays external, same posture as `git`: border vendors the 8.30.1 rule
|
|
397
|
+
config but the binary is yours (on `PATH` or `~/.local/bin/gitleaks`; see the
|
|
398
|
+
requirements paragraph at the top and `src/engines/ADAPTER-CONTRACT.md`). Missing
|
|
399
|
+
gitleaks means `border check` records a CRITICAL `DEGRADED-ENGINE` finding and exits 2,
|
|
400
|
+
and `border scan` exits 2 with `engine binary 'gitleaks' not found; border fails
|
|
401
|
+
closed`. Neither command degrades to a skip.
|
|
402
|
+
- **trufflehog** — optional third engine, external binary, same posture as gitleaks once
|
|
403
|
+
enabled: `engines.trufflehog: true` puts it in the required set, and an absent binary
|
|
404
|
+
degrades the run to exit 2 exactly like a missing gitleaks.
|
|
405
|
+
|
|
406
|
+
There is no "engine absent ⇒ leg silently skipped" state anywhere: missing required
|
|
407
|
+
engines are exit 2 (`2` = the gate could not answer, see
|
|
408
|
+
[Exit codes](#exit-codes-the-contract)), and the ledger refuses to write a PASS for a
|
|
409
|
+
degraded run.
|
|
410
|
+
|
|
378
411
|
## Configuration
|
|
379
412
|
|
|
380
413
|
Everything border can be told lives in `border.yaml` at the repo root. Unknown keys are
|
|
@@ -541,6 +574,19 @@ same one that wrote the commit) how to run the five subcommands, how to produce
|
|
|
541
574
|
|
|
542
575
|
## Changelog
|
|
543
576
|
|
|
577
|
+
### 0.3.2 (2026-09-10)
|
|
578
|
+
- Fix: CLI invoked through the npm `.bin` shim silently exited 0 without running
|
|
579
|
+
(entrypoint detection now realpath-canonicalizes both sides). Regression-locked by a
|
|
580
|
+
pack -> install -> shim-invocation test (`BORDER_PACK_TEST=1`, wired into CI).
|
|
581
|
+
- Fix: `publint` is an exact-pinned runtime dependency, so `border scan`/npm checks work
|
|
582
|
+
out of the box in consumer installs; README documents the engine posture truthfully.
|
|
583
|
+
- Add: release-coherence seed test pinning the scan User-Agent version to package.json.
|
|
584
|
+
|
|
585
|
+
### 0.3.1 (2026-09-10)
|
|
586
|
+
- Add: `border scan [ecosystem:]name@version` — static residue inspection of third-party
|
|
587
|
+
registry packages (npm/PyPI/crates.io/RubyGems), ledger-free by design.
|
|
588
|
+
- Docs: README scan section; crates envelope normalization now logs scrubbed-file hashes.
|
|
589
|
+
|
|
544
590
|
### 0.3.0 (2026-09-09)
|
|
545
591
|
|
|
546
592
|
- **Residue gate (artifact stage).** The blanket install-hook CRITICAL becomes a classified
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "border-customs",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
|
-
"name": "border",
|
|
9
|
-
"version": "0.1
|
|
8
|
+
"name": "border-customs",
|
|
9
|
+
"version": "0.3.1",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@secretlint/core": "13.0.5",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"@secretlint/secretlint-rule-no-homedir": "13.0.5",
|
|
15
15
|
"@secretlint/secretlint-rule-pattern": "13.0.5",
|
|
16
16
|
"@secretlint/secretlint-rule-preset-recommend": "13.0.5",
|
|
17
|
+
"publint": "0.3.24",
|
|
17
18
|
"yaml": "2.9.0",
|
|
18
19
|
"zod": "4.5.4"
|
|
19
20
|
},
|
|
@@ -23,7 +24,6 @@
|
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/node": "22.19.0",
|
|
25
26
|
"esbuild": "0.25.12",
|
|
26
|
-
"publint": "0.3.24",
|
|
27
27
|
"secretlint": "13.0.5",
|
|
28
28
|
"typescript": "5.9.3",
|
|
29
29
|
"verdaccio": "6.10.2"
|
|
@@ -601,7 +601,6 @@
|
|
|
601
601
|
"version": "0.1.7",
|
|
602
602
|
"resolved": "https://registry.npmjs.org/@publint/pack/-/pack-0.1.7.tgz",
|
|
603
603
|
"integrity": "sha512-4EDEmvxWtgsCnnVeBvtFIFZtUhPPt1+bA9JrSwU4Sa//6oKtzCSlGGXYJr44OD9aGISymbieJ4mCKHUygUDU+g==",
|
|
604
|
-
"dev": true,
|
|
605
604
|
"license": "MIT",
|
|
606
605
|
"dependencies": {
|
|
607
606
|
"tinyexec": "^1.3.0"
|
|
@@ -3821,7 +3820,6 @@
|
|
|
3821
3820
|
"version": "1.2.0",
|
|
3822
3821
|
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
|
3823
3822
|
"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
|
|
3824
|
-
"dev": true,
|
|
3825
3823
|
"license": "MIT",
|
|
3826
3824
|
"engines": {
|
|
3827
3825
|
"node": ">=4"
|
|
@@ -3982,7 +3980,6 @@
|
|
|
3982
3980
|
"version": "1.8.0",
|
|
3983
3981
|
"resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz",
|
|
3984
3982
|
"integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==",
|
|
3985
|
-
"dev": true,
|
|
3986
3983
|
"license": "MIT"
|
|
3987
3984
|
},
|
|
3988
3985
|
"node_modules/pako": {
|
|
@@ -4073,7 +4070,6 @@
|
|
|
4073
4070
|
"version": "1.1.1",
|
|
4074
4071
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
|
4075
4072
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
|
4076
|
-
"dev": true,
|
|
4077
4073
|
"license": "ISC"
|
|
4078
4074
|
},
|
|
4079
4075
|
"node_modules/picomatch": {
|
|
@@ -4255,7 +4251,6 @@
|
|
|
4255
4251
|
"version": "0.3.24",
|
|
4256
4252
|
"resolved": "https://registry.npmjs.org/publint/-/publint-0.3.24.tgz",
|
|
4257
4253
|
"integrity": "sha512-9zS56KrKBoqi5Qt8h92uMP8TTM9AYZSgnmCo4u2priMqkOZvQnTsziZ2p5LJ2ywbYkAjoCDp2jda9u4cgFefIw==",
|
|
4258
|
-
"dev": true,
|
|
4259
4254
|
"license": "MIT",
|
|
4260
4255
|
"dependencies": {
|
|
4261
4256
|
"@publint/pack": "^0.1.7",
|
|
@@ -4551,7 +4546,6 @@
|
|
|
4551
4546
|
"version": "1.8.1",
|
|
4552
4547
|
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
|
|
4553
4548
|
"integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
|
|
4554
|
-
"dev": true,
|
|
4555
4549
|
"license": "MIT",
|
|
4556
4550
|
"dependencies": {
|
|
4557
4551
|
"mri": "^1.1.0"
|
|
@@ -5231,7 +5225,6 @@
|
|
|
5231
5225
|
"version": "1.3.1",
|
|
5232
5226
|
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz",
|
|
5233
5227
|
"integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==",
|
|
5234
|
-
"dev": true,
|
|
5235
5228
|
"license": "MIT",
|
|
5236
5229
|
"engines": {
|
|
5237
5230
|
"node": ">=18"
|
package/dist/index.js
CHANGED
|
@@ -13028,7 +13028,8 @@ var require_micromatch = __commonJS({
|
|
|
13028
13028
|
});
|
|
13029
13029
|
|
|
13030
13030
|
// src/index.ts
|
|
13031
|
-
import {
|
|
13031
|
+
import { realpathSync } from "node:fs";
|
|
13032
|
+
import { fileURLToPath as fileURLToPath5, pathToFileURL } from "node:url";
|
|
13032
13033
|
|
|
13033
13034
|
// src/cli.ts
|
|
13034
13035
|
import { parseArgs } from "node:util";
|
|
@@ -37896,10 +37897,14 @@ var NPM_TARGET_LABEL = "artifact";
|
|
|
37896
37897
|
var DEFAULT_TOOL_TIMEOUT_MS = 12e4;
|
|
37897
37898
|
var MESSAGE_TAIL_CHARS = 600;
|
|
37898
37899
|
var HERE = dirname3(fileURLToPath3(import.meta.url));
|
|
37899
|
-
|
|
37900
|
-
|
|
37901
|
-
|
|
37902
|
-
|
|
37900
|
+
function publintLocalBins(here) {
|
|
37901
|
+
return [
|
|
37902
|
+
join6(here, "..", "node_modules", ".bin", "publint"),
|
|
37903
|
+
join6(here, "..", "..", "node_modules", ".bin", "publint"),
|
|
37904
|
+
join6(here, "..", "..", ".bin", "publint")
|
|
37905
|
+
];
|
|
37906
|
+
}
|
|
37907
|
+
var PUBLINT_LOCAL_BINS = publintLocalBins(HERE);
|
|
37903
37908
|
function tail(text) {
|
|
37904
37909
|
const trimmed = text.trim();
|
|
37905
37910
|
return trimmed.length > MESSAGE_TAIL_CHARS ? `${trimmed.slice(-MESSAGE_TAIL_CHARS)}\u2026` : trimmed;
|
|
@@ -43465,7 +43470,7 @@ import { createHash as createHash15 } from "node:crypto";
|
|
|
43465
43470
|
import { basename as basename4 } from "node:path";
|
|
43466
43471
|
var SCAN_MAX_ARTIFACT_BYTES = 200 * 1024 * 1024;
|
|
43467
43472
|
var SCAN_REQUEST_TIMEOUT_MS = 6e4;
|
|
43468
|
-
var SCAN_USER_AGENT = "border-customs/0.3.
|
|
43473
|
+
var SCAN_USER_AGENT = "border-customs/0.3.2 (+https://github.com/sumteclab/border; border scan)";
|
|
43469
43474
|
var CRATES_DOWNLOAD_HOST = "https://static.crates.io";
|
|
43470
43475
|
var defaultScanFetcher = async (url2, init) => {
|
|
43471
43476
|
if (!/^https?:\/\//.test(url2)) throw new EngineRunError(`border scan refuses non-http(s) URL: ${url2}`, null);
|
|
@@ -43986,11 +43991,18 @@ function run(argv, stdout = (line) => {
|
|
|
43986
43991
|
}
|
|
43987
43992
|
|
|
43988
43993
|
// src/index.ts
|
|
43989
|
-
function isMainModule() {
|
|
43990
|
-
|
|
43991
|
-
|
|
43994
|
+
function isMainModule({ argv1, moduleUrl, realpath }) {
|
|
43995
|
+
if (argv1 === void 0) return false;
|
|
43996
|
+
const canonical = (p) => {
|
|
43997
|
+
try {
|
|
43998
|
+
return realpath(p);
|
|
43999
|
+
} catch {
|
|
44000
|
+
return p;
|
|
44001
|
+
}
|
|
44002
|
+
};
|
|
44003
|
+
return pathToFileURL(canonical(argv1)).href === pathToFileURL(canonical(fileURLToPath5(moduleUrl))).href;
|
|
43992
44004
|
}
|
|
43993
|
-
if (isMainModule()) {
|
|
44005
|
+
if (isMainModule({ argv1: process.argv[1], moduleUrl: import.meta.url, realpath: realpathSync.native })) {
|
|
43994
44006
|
const outcome = run(process.argv.slice(2));
|
|
43995
44007
|
process.exitCode = outcome instanceof Promise ? await outcome : outcome;
|
|
43996
44008
|
}
|
|
@@ -43999,6 +44011,7 @@ export {
|
|
|
43999
44011
|
EXIT_ERROR,
|
|
44000
44012
|
EXIT_PASS,
|
|
44001
44013
|
SUBCOMMANDS,
|
|
44014
|
+
isMainModule,
|
|
44002
44015
|
run,
|
|
44003
44016
|
translateError,
|
|
44004
44017
|
usage
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "border-customs",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
|
+
"//": "publint is a runtime dep (exact pin): the npm-stage engine is invoked via bin resolution against the installed layout, so consumers get the publint-fail leg out of the box; the dev suite resolves the same node_modules/.bin/publint — no devDependencies entry is needed for it to stay green.",
|
|
4
5
|
"description": "Fail-closed git push gate: secret scanning + supply-chain checks before any remote leaves the machine",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"bin": {
|
|
@@ -30,13 +31,13 @@
|
|
|
30
31
|
"@secretlint/secretlint-rule-no-homedir": "13.0.5",
|
|
31
32
|
"@secretlint/secretlint-rule-pattern": "13.0.5",
|
|
32
33
|
"@secretlint/secretlint-rule-preset-recommend": "13.0.5",
|
|
34
|
+
"publint": "0.3.24",
|
|
33
35
|
"yaml": "2.9.0",
|
|
34
36
|
"zod": "4.5.4"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
39
|
"@types/node": "22.19.0",
|
|
38
40
|
"esbuild": "0.25.12",
|
|
39
|
-
"publint": "0.3.24",
|
|
40
41
|
"secretlint": "13.0.5",
|
|
41
42
|
"typescript": "5.9.3",
|
|
42
43
|
"verdaccio": "6.10.2"
|