@vola-trebla/croak 0.1.1 → 0.1.3
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/dist/cli.js +1 -1
- package/dist/install/detectors.js +30 -23
- package/dist/install/metadata.js +4 -1
- package/package.json +1 -1
- package/scripts/croak-hook.sh +4 -2
- package/scripts/croak-install.sh +14 -6
package/dist/cli.js
CHANGED
|
@@ -18,7 +18,7 @@ import { anthropicTriage, DEFAULT_MODEL } from "./llm/client.js";
|
|
|
18
18
|
import { renderScan, scanToJson } from "./repo-report.js";
|
|
19
19
|
import { ScanTargetError, scanRepoAt } from "./scan.js";
|
|
20
20
|
import { aggregate } from "./verdict.js";
|
|
21
|
-
export const VERSION = "0.1.
|
|
21
|
+
export const VERSION = "0.1.3";
|
|
22
22
|
export const EXIT = { ok: 0, block: 1, usage: 2, incomplete: 3 };
|
|
23
23
|
export const EXIT_USAGE = EXIT.usage;
|
|
24
24
|
const DEFAULT_DEPS = {
|
|
@@ -33,29 +33,36 @@ function sourceProblem(resolved, name, version) {
|
|
|
33
33
|
return `is downloaded over ${url.protocol.replace(":", "")}, not https`;
|
|
34
34
|
return `comes from ${url.host}${url.username === "" ? "" : " with credentials in the URL"}, not ${REGISTRY}`;
|
|
35
35
|
}
|
|
36
|
-
export const source = (changes) =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
36
|
+
export const source = (changes) => {
|
|
37
|
+
const folders = new Set(changes.map((c) => c.entry.path));
|
|
38
|
+
return changes.flatMap((c) => {
|
|
39
|
+
if (c.entry.link &&
|
|
40
|
+
c.entry.resolved !== undefined &&
|
|
41
|
+
folders.has(c.entry.resolved))
|
|
42
|
+
return [];
|
|
43
|
+
if (c.entry.link) {
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
ruleId: "install/source",
|
|
47
|
+
severity: "info",
|
|
48
|
+
file: label(c),
|
|
49
|
+
message: "is a workspace link, not a download",
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
const problem = sourceProblem(c.entry.resolved, c.entry.name, c.entry.version);
|
|
54
|
+
return problem === undefined
|
|
55
|
+
? []
|
|
56
|
+
: [
|
|
57
|
+
{
|
|
58
|
+
ruleId: "install/source",
|
|
59
|
+
severity: "warn",
|
|
60
|
+
file: label(c),
|
|
61
|
+
message: `${what(c)} and ${problem}`,
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
});
|
|
65
|
+
};
|
|
59
66
|
export const integrity = (changes) => changes
|
|
60
67
|
.filter((c) => !c.entry.link &&
|
|
61
68
|
c.entry.resolved !== undefined &&
|
package/dist/install/metadata.js
CHANGED
|
@@ -79,7 +79,10 @@ export function metadataFindings(change, facts, now = new Date()) {
|
|
|
79
79
|
? "; with an install script, this is the pattern of a hijacked maintainer account"
|
|
80
80
|
: "";
|
|
81
81
|
if (recentRelease) {
|
|
82
|
-
|
|
82
|
+
const when = facts.ageDays !== undefined && facts.ageDays < 0
|
|
83
|
+
? "has a publish time after this machine's clock"
|
|
84
|
+
: `was published ${facts.ageDays} day(s) ago`;
|
|
85
|
+
findings.push(finding("install/recent-release", "warn", `${when}; new versions of popular packages are where hijacks land first`));
|
|
83
86
|
}
|
|
84
87
|
if (provenanceDropped) {
|
|
85
88
|
findings.push(finding("install/provenance", hijack ? "block" : "warn", `has no provenance, although earlier versions were published with it${pattern}`));
|
package/package.json
CHANGED
package/scripts/croak-hook.sh
CHANGED
|
@@ -15,8 +15,10 @@ here=$(dirname "$self")
|
|
|
15
15
|
cli="$here/../dist/cli.js"
|
|
16
16
|
install="$here/croak-install.sh"
|
|
17
17
|
[ -f "$cli" ] || deny "$cli not found (run pnpm build in the croak repo)"
|
|
18
|
-
#
|
|
19
|
-
#
|
|
18
|
+
# croak needs Node 24.2 or later (package.json engines). On a Node whose
|
|
19
|
+
# entry point check does not run, the CLI exits 0 with no output, and every
|
|
20
|
+
# step would "succeed" silently; on others, behaviour croak was never tested
|
|
21
|
+
# with would decide. Either way: deny instead.
|
|
20
22
|
node -e 'const [a, b] = process.versions.node.split(".").map(Number); process.exit(a > 24 || (a === 24 && b >= 2) ? 0 : 1)' ||
|
|
21
23
|
deny "croak needs Node 24.2 or later (found $(node --version 2>/dev/null || echo none))"
|
|
22
24
|
|
package/scripts/croak-install.sh
CHANGED
|
@@ -229,12 +229,20 @@ if ! isolated "$@" >"$work/plan.log" 2>&1; then
|
|
|
229
229
|
tail -n 15 "$work/plan.log" >&2
|
|
230
230
|
exit 3
|
|
231
231
|
fi
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
232
|
+
# The copy holds package.json files only, so a lockfile in the member's folder
|
|
233
|
+
# means the package manager planned that folder apart from the workspace, as
|
|
234
|
+
# the real install will. Decide by that, never by the root lockfile, which the
|
|
235
|
+
# package manager then left as it was and the repository may have forged (D52).
|
|
236
|
+
if [ -n "$member" ] && [ -f "$work/plan/$member/$lockname" ]; then
|
|
237
|
+
if [ "$manager" != npm ]; then
|
|
238
|
+
# pnpm writes one for a real member too (sharedWorkspaceLockfile: false);
|
|
239
|
+
# planning it alone would leave the rest of the workspace out.
|
|
240
|
+
echo "croak-install: $manager planned $member apart from its workspace, so this install was not checked" >&2
|
|
241
|
+
exit 3
|
|
242
|
+
fi
|
|
243
|
+
# npm installs a folder the workspace does not list as a project of its own.
|
|
244
|
+
# That plan lacked the folder's own lockfile and sanitized manifest, so plan
|
|
245
|
+
# the folder again, exactly as outside any workspace (D48).
|
|
238
246
|
cd "$start"
|
|
239
247
|
if [ "$manager" = npm ] && [ -f npm-shrinkwrap.json ]; then
|
|
240
248
|
echo "croak-install: npm-shrinkwrap.json is not supported yet, so this install was not checked" >&2
|