calllint 0.8.0 → 0.9.1
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/index.js +417 -5
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { readFileSync as
|
|
4
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
6
|
|
|
7
7
|
// src/args.ts
|
|
@@ -4472,6 +4472,389 @@ function receiptCommand(args, deps) {
|
|
|
4472
4472
|
return { stdout, exitCode: EXIT.OK };
|
|
4473
4473
|
}
|
|
4474
4474
|
|
|
4475
|
+
// src/commands/action.ts
|
|
4476
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
4477
|
+
import { resolve as resolve5 } from "node:path";
|
|
4478
|
+
|
|
4479
|
+
// ../../packages/action-analyzer/src/types.ts
|
|
4480
|
+
var KIND_RISK_PROFILES = {
|
|
4481
|
+
"email.reply": ["ACTION", "SECRETS", "PROMPT"],
|
|
4482
|
+
"email.forward": ["ACTION", "SECRETS", "FILES", "PROMPT"],
|
|
4483
|
+
"message.post": ["ACTION", "SECRETS"],
|
|
4484
|
+
"a2a.delegate": ["ACTION", "NETWORK", "PROMPT"],
|
|
4485
|
+
"payment.authorize": ["MONEY", "SECRETS"],
|
|
4486
|
+
"account.register": ["ACTION", "SECRETS", "SUPPLY"],
|
|
4487
|
+
"github.write": ["ACTION", "SUPPLY"],
|
|
4488
|
+
"npm.publish": ["ACTION", "SUPPLY", "MONEY"],
|
|
4489
|
+
"cloud.modify": ["ACTION", "EXEC", "MONEY"]
|
|
4490
|
+
};
|
|
4491
|
+
|
|
4492
|
+
// ../../packages/action-analyzer/src/analyzeAction.ts
|
|
4493
|
+
function analyzeAction(descriptor) {
|
|
4494
|
+
const findings = [];
|
|
4495
|
+
if (descriptor.schema_version !== "calllint.action.v0") {
|
|
4496
|
+
findings.push({
|
|
4497
|
+
id: "action.unknown-schema-version",
|
|
4498
|
+
title: "Unknown action schema version",
|
|
4499
|
+
severity: "medium",
|
|
4500
|
+
blocker: false,
|
|
4501
|
+
symbol: "ACTION",
|
|
4502
|
+
riskClass: "S3",
|
|
4503
|
+
mode: "OBSERVED",
|
|
4504
|
+
confidence: "high",
|
|
4505
|
+
detectionMethod: "config-analysis",
|
|
4506
|
+
evidence: [{
|
|
4507
|
+
type: "config",
|
|
4508
|
+
path: "schema_version",
|
|
4509
|
+
value: descriptor.schema_version
|
|
4510
|
+
}],
|
|
4511
|
+
impact: `Unknown action schema version: ${descriptor.schema_version}`,
|
|
4512
|
+
fix: "Use calllint.action.v0 schema version"
|
|
4513
|
+
});
|
|
4514
|
+
return findings;
|
|
4515
|
+
}
|
|
4516
|
+
const riskProfile = KIND_RISK_PROFILES[descriptor.kind];
|
|
4517
|
+
if (!riskProfile) {
|
|
4518
|
+
findings.push({
|
|
4519
|
+
id: "action.unknown-kind",
|
|
4520
|
+
title: "Unknown action kind",
|
|
4521
|
+
severity: "medium",
|
|
4522
|
+
blocker: false,
|
|
4523
|
+
symbol: "ACTION",
|
|
4524
|
+
riskClass: "S3",
|
|
4525
|
+
mode: "OBSERVED",
|
|
4526
|
+
confidence: "high",
|
|
4527
|
+
detectionMethod: "config-analysis",
|
|
4528
|
+
evidence: [{
|
|
4529
|
+
type: "config",
|
|
4530
|
+
path: "kind",
|
|
4531
|
+
value: descriptor.kind
|
|
4532
|
+
}],
|
|
4533
|
+
impact: `Unknown action kind: ${descriptor.kind}`,
|
|
4534
|
+
fix: "Use one of the 9 defined action kinds (email.reply, email.forward, message.post, a2a.delegate, payment.authorize, account.register, github.write, npm.publish, cloud.modify)"
|
|
4535
|
+
});
|
|
4536
|
+
return findings;
|
|
4537
|
+
}
|
|
4538
|
+
switch (descriptor.kind) {
|
|
4539
|
+
case "email.reply":
|
|
4540
|
+
case "email.forward":
|
|
4541
|
+
findings.push(...analyzeEmail(descriptor));
|
|
4542
|
+
break;
|
|
4543
|
+
case "message.post":
|
|
4544
|
+
findings.push(...analyzeMessaging(descriptor));
|
|
4545
|
+
break;
|
|
4546
|
+
case "a2a.delegate":
|
|
4547
|
+
findings.push(...analyzeDelegate(descriptor));
|
|
4548
|
+
break;
|
|
4549
|
+
case "payment.authorize":
|
|
4550
|
+
findings.push(...analyzePayment(descriptor));
|
|
4551
|
+
break;
|
|
4552
|
+
case "account.register":
|
|
4553
|
+
findings.push(...analyzeRegistration(descriptor));
|
|
4554
|
+
break;
|
|
4555
|
+
case "github.write":
|
|
4556
|
+
findings.push(...analyzeGitHub(descriptor));
|
|
4557
|
+
break;
|
|
4558
|
+
case "npm.publish":
|
|
4559
|
+
findings.push(...analyzeNpmPublish(descriptor));
|
|
4560
|
+
break;
|
|
4561
|
+
case "cloud.modify":
|
|
4562
|
+
findings.push(...analyzeCloud(descriptor));
|
|
4563
|
+
break;
|
|
4564
|
+
}
|
|
4565
|
+
findings.push(...analyzeSecretShapedHeaders(descriptor));
|
|
4566
|
+
return findings;
|
|
4567
|
+
}
|
|
4568
|
+
function analyzeEmail(descriptor) {
|
|
4569
|
+
const findings = [];
|
|
4570
|
+
const metadata = descriptor.metadata || {};
|
|
4571
|
+
const hasAttachments = descriptor.parameters.has_attachments;
|
|
4572
|
+
const attachmentHashes = metadata.attachment_hashes || [];
|
|
4573
|
+
if (hasAttachments && attachmentHashes.length === 0) {
|
|
4574
|
+
findings.push({
|
|
4575
|
+
id: "action.unverified-attachment",
|
|
4576
|
+
title: "Unverified email attachments",
|
|
4577
|
+
severity: "high",
|
|
4578
|
+
blocker: false,
|
|
4579
|
+
symbol: "FILES",
|
|
4580
|
+
riskClass: "S2",
|
|
4581
|
+
mode: "OBSERVED",
|
|
4582
|
+
confidence: "high",
|
|
4583
|
+
detectionMethod: "config-analysis",
|
|
4584
|
+
evidence: [{
|
|
4585
|
+
type: "config",
|
|
4586
|
+
path: "parameters.has_attachments",
|
|
4587
|
+
value: String(hasAttachments)
|
|
4588
|
+
}],
|
|
4589
|
+
impact: "Email action has attachments but no attachment_hashes provided",
|
|
4590
|
+
fix: "Provide SHA-256 hashes for all attachments in metadata.attachment_hashes"
|
|
4591
|
+
});
|
|
4592
|
+
}
|
|
4593
|
+
return findings;
|
|
4594
|
+
}
|
|
4595
|
+
function analyzeMessaging(descriptor) {
|
|
4596
|
+
const findings = [];
|
|
4597
|
+
return findings;
|
|
4598
|
+
}
|
|
4599
|
+
function analyzeDelegate(descriptor) {
|
|
4600
|
+
const findings = [];
|
|
4601
|
+
const metadata = descriptor.metadata || {};
|
|
4602
|
+
const delegateTarget = metadata.delegate_target;
|
|
4603
|
+
if (!delegateTarget) {
|
|
4604
|
+
findings.push({
|
|
4605
|
+
id: "action.missing-delegate-target",
|
|
4606
|
+
title: "Missing delegate target",
|
|
4607
|
+
severity: "high",
|
|
4608
|
+
blocker: false,
|
|
4609
|
+
symbol: "NETWORK",
|
|
4610
|
+
riskClass: "S3",
|
|
4611
|
+
mode: "OBSERVED",
|
|
4612
|
+
confidence: "high",
|
|
4613
|
+
detectionMethod: "config-analysis",
|
|
4614
|
+
evidence: [{
|
|
4615
|
+
type: "config",
|
|
4616
|
+
path: "metadata.delegate_target",
|
|
4617
|
+
value: "null"
|
|
4618
|
+
}],
|
|
4619
|
+
impact: "Agent delegation missing delegate_target in metadata",
|
|
4620
|
+
fix: "Specify the delegate target (agent ID, API endpoint, or service name)"
|
|
4621
|
+
});
|
|
4622
|
+
} else if (typeof delegateTarget === "string" && delegateTarget.startsWith("http://")) {
|
|
4623
|
+
findings.push({
|
|
4624
|
+
id: "action.insecure-delegate-target",
|
|
4625
|
+
title: "Insecure delegate target",
|
|
4626
|
+
severity: "medium",
|
|
4627
|
+
blocker: false,
|
|
4628
|
+
symbol: "NETWORK",
|
|
4629
|
+
riskClass: "S3",
|
|
4630
|
+
mode: "OBSERVED",
|
|
4631
|
+
confidence: "high",
|
|
4632
|
+
detectionMethod: "config-analysis",
|
|
4633
|
+
evidence: [{
|
|
4634
|
+
type: "config",
|
|
4635
|
+
path: "metadata.delegate_target",
|
|
4636
|
+
value: delegateTarget
|
|
4637
|
+
}],
|
|
4638
|
+
impact: "Delegate target uses insecure HTTP",
|
|
4639
|
+
fix: "Use HTTPS for delegate targets"
|
|
4640
|
+
});
|
|
4641
|
+
}
|
|
4642
|
+
return findings;
|
|
4643
|
+
}
|
|
4644
|
+
function analyzePayment(descriptor) {
|
|
4645
|
+
const findings = [];
|
|
4646
|
+
const metadata = descriptor.metadata || {};
|
|
4647
|
+
const amount = metadata.amount;
|
|
4648
|
+
if (typeof amount === "number" && amount > 0) {
|
|
4649
|
+
findings.push({
|
|
4650
|
+
id: "action.financial-observed",
|
|
4651
|
+
title: "Financial transaction observed",
|
|
4652
|
+
severity: "high",
|
|
4653
|
+
blocker: false,
|
|
4654
|
+
symbol: "MONEY",
|
|
4655
|
+
riskClass: "S5",
|
|
4656
|
+
mode: "OBSERVED",
|
|
4657
|
+
confidence: "high",
|
|
4658
|
+
detectionMethod: "config-analysis",
|
|
4659
|
+
evidence: [{
|
|
4660
|
+
type: "config",
|
|
4661
|
+
path: "metadata.amount",
|
|
4662
|
+
value: String(amount)
|
|
4663
|
+
}],
|
|
4664
|
+
impact: `Payment authorization for ${metadata.currency || "unknown currency"} ${amount}`,
|
|
4665
|
+
fix: "Verify payment amount and recipient before authorizing"
|
|
4666
|
+
});
|
|
4667
|
+
}
|
|
4668
|
+
return findings;
|
|
4669
|
+
}
|
|
4670
|
+
function analyzeRegistration(descriptor) {
|
|
4671
|
+
const findings = [];
|
|
4672
|
+
return findings;
|
|
4673
|
+
}
|
|
4674
|
+
function analyzeGitHub(descriptor) {
|
|
4675
|
+
const findings = [];
|
|
4676
|
+
return findings;
|
|
4677
|
+
}
|
|
4678
|
+
function analyzeNpmPublish(descriptor) {
|
|
4679
|
+
const findings = [];
|
|
4680
|
+
return findings;
|
|
4681
|
+
}
|
|
4682
|
+
function analyzeCloud(descriptor) {
|
|
4683
|
+
const findings = [];
|
|
4684
|
+
return findings;
|
|
4685
|
+
}
|
|
4686
|
+
function analyzeSecretShapedHeaders(descriptor) {
|
|
4687
|
+
const findings = [];
|
|
4688
|
+
const metadata = descriptor.metadata || {};
|
|
4689
|
+
const headerKeys = metadata.header_keys || [];
|
|
4690
|
+
const secretPatterns = ["authorization", "api-key", "api_key", "token", "secret", "password", "bearer"];
|
|
4691
|
+
for (const key of headerKeys) {
|
|
4692
|
+
const lowerKey = key.toLowerCase();
|
|
4693
|
+
if (secretPatterns.some((pattern) => lowerKey.includes(pattern))) {
|
|
4694
|
+
findings.push({
|
|
4695
|
+
id: "secrets.env-key",
|
|
4696
|
+
title: "Secret-shaped header key",
|
|
4697
|
+
severity: "medium",
|
|
4698
|
+
blocker: false,
|
|
4699
|
+
symbol: "SECRETS",
|
|
4700
|
+
riskClass: "S2",
|
|
4701
|
+
mode: "OBSERVED",
|
|
4702
|
+
confidence: "medium",
|
|
4703
|
+
detectionMethod: "config-analysis",
|
|
4704
|
+
evidence: [{
|
|
4705
|
+
type: "config",
|
|
4706
|
+
path: "metadata.header_keys",
|
|
4707
|
+
value: key
|
|
4708
|
+
}],
|
|
4709
|
+
impact: `Header key resembles a secret: ${key}`,
|
|
4710
|
+
fix: "Verify that secret headers are properly secured and not logged"
|
|
4711
|
+
});
|
|
4712
|
+
break;
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
return findings;
|
|
4716
|
+
}
|
|
4717
|
+
|
|
4718
|
+
// src/commands/action.ts
|
|
4719
|
+
function actionCommand(args, deps) {
|
|
4720
|
+
const subcommand = args.positionals[0];
|
|
4721
|
+
if (!subcommand || subcommand === "help") {
|
|
4722
|
+
return {
|
|
4723
|
+
stdout: actionHelp(),
|
|
4724
|
+
stderr: "",
|
|
4725
|
+
exitCode: 0
|
|
4726
|
+
};
|
|
4727
|
+
}
|
|
4728
|
+
if (subcommand === "inspect") {
|
|
4729
|
+
return actionInspect(args, deps);
|
|
4730
|
+
}
|
|
4731
|
+
return {
|
|
4732
|
+
stdout: "",
|
|
4733
|
+
stderr: `Unknown action subcommand: ${subcommand}
|
|
4734
|
+
Run \`calllint action help\`.`,
|
|
4735
|
+
exitCode: 2
|
|
4736
|
+
};
|
|
4737
|
+
}
|
|
4738
|
+
function actionInspect(args, deps) {
|
|
4739
|
+
const actionFile = args.positionals[1];
|
|
4740
|
+
if (!actionFile) {
|
|
4741
|
+
return {
|
|
4742
|
+
stdout: "",
|
|
4743
|
+
stderr: "Error: Missing action file\nUsage: calllint action inspect <file.json>",
|
|
4744
|
+
exitCode: 2
|
|
4745
|
+
};
|
|
4746
|
+
}
|
|
4747
|
+
try {
|
|
4748
|
+
const absolutePath = resolve5(deps.cwd, actionFile);
|
|
4749
|
+
const content = readFileSync9(absolutePath, "utf-8");
|
|
4750
|
+
const descriptor = JSON.parse(content);
|
|
4751
|
+
if (descriptor.schema_version !== "calllint.action.v0") {
|
|
4752
|
+
return {
|
|
4753
|
+
stdout: "",
|
|
4754
|
+
stderr: `Error: Unsupported schema version: ${descriptor.schema_version}
|
|
4755
|
+
Expected: calllint.action.v0`,
|
|
4756
|
+
exitCode: 2
|
|
4757
|
+
};
|
|
4758
|
+
}
|
|
4759
|
+
const policyPath = args.flags["policy"];
|
|
4760
|
+
const policy = loadPolicyOrDefault(policyPath ? resolve5(deps.cwd, policyPath) : void 0);
|
|
4761
|
+
const findings = analyzeAction(descriptor);
|
|
4762
|
+
const verdict = computeActionVerdict(findings, policy);
|
|
4763
|
+
let stdout = "";
|
|
4764
|
+
if (args.flags["json"]) {
|
|
4765
|
+
const report = {
|
|
4766
|
+
schema_version: "calllint.action-report.v0",
|
|
4767
|
+
verdict,
|
|
4768
|
+
findings,
|
|
4769
|
+
target: {
|
|
4770
|
+
type: "action",
|
|
4771
|
+
kind: descriptor.kind,
|
|
4772
|
+
schema_version: descriptor.schema_version
|
|
4773
|
+
},
|
|
4774
|
+
policy_applied: "default",
|
|
4775
|
+
scan_timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4776
|
+
};
|
|
4777
|
+
stdout = JSON.stringify(report, null, 2);
|
|
4778
|
+
} else {
|
|
4779
|
+
const header = `
|
|
4780
|
+
\u{1F50D} Action: ${descriptor.kind}
|
|
4781
|
+
`;
|
|
4782
|
+
const verdictEmoji = verdict === "SAFE" ? "\u2705" : verdict === "BLOCK" ? "\u26D4" : verdict === "REVIEW" ? "\u26A0\uFE0F" : "\u25C7";
|
|
4783
|
+
const verdictLine = `Verdict: ${verdictEmoji} ${verdict}
|
|
4784
|
+
`;
|
|
4785
|
+
const findingsLine = `Findings: ${findings.length}
|
|
4786
|
+
`;
|
|
4787
|
+
let body = verdictLine + findingsLine;
|
|
4788
|
+
if (findings.length > 0) {
|
|
4789
|
+
body += "\n";
|
|
4790
|
+
for (const f of findings) {
|
|
4791
|
+
body += ` \u2022 ${f.id}: ${f.impact}
|
|
4792
|
+
`;
|
|
4793
|
+
}
|
|
4794
|
+
}
|
|
4795
|
+
stdout = header + body;
|
|
4796
|
+
}
|
|
4797
|
+
return {
|
|
4798
|
+
stdout,
|
|
4799
|
+
stderr: "",
|
|
4800
|
+
exitCode: verdict === "SAFE" ? 0 : 1
|
|
4801
|
+
};
|
|
4802
|
+
} catch (error) {
|
|
4803
|
+
const err = error;
|
|
4804
|
+
let stderr = "";
|
|
4805
|
+
if (err.code === "ENOENT") {
|
|
4806
|
+
stderr = `Error: File not found: ${actionFile}`;
|
|
4807
|
+
} else if (error instanceof SyntaxError) {
|
|
4808
|
+
stderr = `Error: Invalid JSON in ${actionFile}
|
|
4809
|
+
${error.message}`;
|
|
4810
|
+
} else {
|
|
4811
|
+
stderr = `Error: ${err.message}`;
|
|
4812
|
+
}
|
|
4813
|
+
return {
|
|
4814
|
+
stdout: "",
|
|
4815
|
+
stderr,
|
|
4816
|
+
exitCode: 2
|
|
4817
|
+
};
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
function actionHelp() {
|
|
4821
|
+
return `calllint action \u2014 Inspect planned external actions
|
|
4822
|
+
|
|
4823
|
+
USAGE
|
|
4824
|
+
calllint action inspect <file.json>
|
|
4825
|
+
calllint action help
|
|
4826
|
+
|
|
4827
|
+
COMMANDS
|
|
4828
|
+
inspect <file> Analyze a calllint.action.v0 descriptor
|
|
4829
|
+
help Show this help
|
|
4830
|
+
|
|
4831
|
+
OPTIONS
|
|
4832
|
+
--json Output JSON report
|
|
4833
|
+
--policy <file> Use custom policy file
|
|
4834
|
+
--no-emoji Disable emoji in output
|
|
4835
|
+
|
|
4836
|
+
EXAMPLE
|
|
4837
|
+
calllint action inspect payment.json
|
|
4838
|
+
calllint action inspect email-reply.json --json
|
|
4839
|
+
|
|
4840
|
+
See: https://calllint.com/docs/action-inspect (ADR 0029)
|
|
4841
|
+
`;
|
|
4842
|
+
}
|
|
4843
|
+
function computeActionVerdict(findings, policy) {
|
|
4844
|
+
if (findings.length === 0) {
|
|
4845
|
+
return "SAFE";
|
|
4846
|
+
}
|
|
4847
|
+
const hasBlocker = findings.some((f) => f.blocker || f.severity === "critical");
|
|
4848
|
+
if (hasBlocker) {
|
|
4849
|
+
return "BLOCK";
|
|
4850
|
+
}
|
|
4851
|
+
const hasHigh = findings.some((f) => f.severity === "high");
|
|
4852
|
+
if (hasHigh) {
|
|
4853
|
+
return "REVIEW";
|
|
4854
|
+
}
|
|
4855
|
+
return "REVIEW";
|
|
4856
|
+
}
|
|
4857
|
+
|
|
4475
4858
|
// src/run.ts
|
|
4476
4859
|
function run(argv, deps) {
|
|
4477
4860
|
const args = parseArgs(argv);
|
|
@@ -4540,6 +4923,8 @@ function run(argv, deps) {
|
|
|
4540
4923
|
return explainCommand(args, { cwd: deps.cwd });
|
|
4541
4924
|
case "receipt":
|
|
4542
4925
|
return receiptCommand(args, { cwd: deps.cwd });
|
|
4926
|
+
case "action":
|
|
4927
|
+
return actionCommand(args, { cwd: deps.cwd });
|
|
4543
4928
|
case "gen-rule":
|
|
4544
4929
|
return genRuleCommand(args, { cwd: deps.cwd });
|
|
4545
4930
|
case "policy":
|
|
@@ -4594,15 +4979,39 @@ async function fetchNpmFacts(packageSpec, fetchJson) {
|
|
|
4594
4979
|
(k) => typeof scripts[k] === "string" && scripts[k].length > 0
|
|
4595
4980
|
);
|
|
4596
4981
|
const deprecated = typeof versionDoc.deprecated === "string" ? versionDoc.deprecated : void 0;
|
|
4982
|
+
const description = typeof versionDoc.description === "string" ? versionDoc.description : void 0;
|
|
4983
|
+
const readme = typeof doc.readme === "string" ? doc.readme : void 0;
|
|
4597
4984
|
return {
|
|
4598
4985
|
name,
|
|
4599
4986
|
versionExists: true,
|
|
4600
4987
|
installScripts,
|
|
4601
4988
|
deprecated,
|
|
4602
4989
|
latestVersion,
|
|
4603
|
-
resolvedVersion
|
|
4990
|
+
resolvedVersion,
|
|
4991
|
+
description,
|
|
4992
|
+
readme
|
|
4604
4993
|
};
|
|
4605
4994
|
}
|
|
4995
|
+
function surfacesFromNpmFacts(facts) {
|
|
4996
|
+
const surfaces = [];
|
|
4997
|
+
if (typeof facts.description === "string" && facts.description.length > 0) {
|
|
4998
|
+
surfaces.push({
|
|
4999
|
+
path: `registry:${facts.name}#description`,
|
|
5000
|
+
kind: "registry-description",
|
|
5001
|
+
text: facts.description,
|
|
5002
|
+
truncated: false
|
|
5003
|
+
});
|
|
5004
|
+
}
|
|
5005
|
+
if (typeof facts.readme === "string" && facts.readme.length > 0) {
|
|
5006
|
+
surfaces.push({
|
|
5007
|
+
path: `registry:${facts.name}#readme`,
|
|
5008
|
+
kind: "registry-readme",
|
|
5009
|
+
text: facts.readme,
|
|
5010
|
+
truncated: false
|
|
5011
|
+
});
|
|
5012
|
+
}
|
|
5013
|
+
return surfaces;
|
|
5014
|
+
}
|
|
4606
5015
|
function findingsFromNpmFacts(facts, fetchedAt) {
|
|
4607
5016
|
const findings = [];
|
|
4608
5017
|
const stamp = (f) => ({ ...f, source: "online", fetchedAt });
|
|
@@ -4660,6 +5069,9 @@ function findingsFromNpmFacts(facts, fetchedAt) {
|
|
|
4660
5069
|
fix: "Migrate to the maintained successor or a supported version."
|
|
4661
5070
|
}));
|
|
4662
5071
|
}
|
|
5072
|
+
for (const f of analyzeDocumentSurfaces(surfacesFromNpmFacts(facts))) {
|
|
5073
|
+
findings.push(stamp(f));
|
|
5074
|
+
}
|
|
4663
5075
|
return findings;
|
|
4664
5076
|
}
|
|
4665
5077
|
async function enrichNpmPackage(packageSpec, fetchJson, fetchedAt) {
|
|
@@ -4786,13 +5198,13 @@ async function breathe(argv, deps = {}) {
|
|
|
4786
5198
|
}
|
|
4787
5199
|
|
|
4788
5200
|
// src/version.ts
|
|
4789
|
-
import { readFileSync as
|
|
5201
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
4790
5202
|
import { fileURLToPath } from "node:url";
|
|
4791
5203
|
import { dirname as dirname4, join as join11 } from "node:path";
|
|
4792
5204
|
function resolveToolVersion() {
|
|
4793
5205
|
try {
|
|
4794
5206
|
const pkgPath = join11(dirname4(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
4795
|
-
const pkg = JSON.parse(
|
|
5207
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
|
|
4796
5208
|
return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : "unknown";
|
|
4797
5209
|
} catch {
|
|
4798
5210
|
return "unknown";
|
|
@@ -4802,7 +5214,7 @@ function resolveToolVersion() {
|
|
|
4802
5214
|
// src/index.ts
|
|
4803
5215
|
function readStdin() {
|
|
4804
5216
|
try {
|
|
4805
|
-
return
|
|
5217
|
+
return readFileSync11(0, "utf8");
|
|
4806
5218
|
} catch {
|
|
4807
5219
|
return "";
|
|
4808
5220
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "calllint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Evidence-backed security verdicts for MCP servers and agent tools. Lint agent tool-call risk before tools run — SAFE / REVIEW / BLOCK / UNKNOWN, with evidence. Never executes the server it judges.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {},
|
|
51
51
|
"devDependencies": {
|
|
52
|
+
"@calllint/action-analyzer": "workspace:*",
|
|
52
53
|
"@calllint/core": "workspace:*",
|
|
53
54
|
"@calllint/fixtures": "workspace:*",
|
|
54
55
|
"@calllint/online": "workspace:*",
|