@intutic/cli 1.10.0 → 2.0.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/README.md +1 -0
- package/dist/cli.js +868 -30
- package/dist/cli.js.map +1 -1
- package/dist/commands/attenuate.d.ts +39 -0
- package/dist/commands/attenuate.d.ts.map +1 -0
- package/dist/commands/attenuate.js +98 -0
- package/dist/commands/attenuate.js.map +1 -0
- package/dist/commands/budget.d.ts +80 -0
- package/dist/commands/budget.d.ts.map +1 -1
- package/dist/commands/budget.js +119 -3
- package/dist/commands/budget.js.map +1 -1
- package/dist/commands/connect.d.ts +10 -0
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/connect.js +358 -48
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/credentials.d.ts +31 -0
- package/dist/commands/credentials.d.ts.map +1 -0
- package/dist/commands/credentials.js +134 -0
- package/dist/commands/credentials.js.map +1 -0
- package/dist/commands/decision.d.ts +37 -0
- package/dist/commands/decision.d.ts.map +1 -0
- package/dist/commands/decision.js +56 -0
- package/dist/commands/decision.js.map +1 -0
- package/dist/commands/doctor.d.ts +38 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +228 -18
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/enforce.d.ts +33 -0
- package/dist/commands/enforce.d.ts.map +1 -0
- package/dist/commands/enforce.js +150 -0
- package/dist/commands/enforce.js.map +1 -0
- package/dist/commands/enterprise.d.ts +29 -0
- package/dist/commands/enterprise.d.ts.map +1 -0
- package/dist/commands/enterprise.js +141 -0
- package/dist/commands/enterprise.js.map +1 -0
- package/dist/commands/env.d.ts +7 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +55 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/exec.d.ts +38 -2
- package/dist/commands/exec.d.ts.map +1 -1
- package/dist/commands/exec.js +247 -10
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/findings.d.ts +84 -0
- package/dist/commands/findings.d.ts.map +1 -0
- package/dist/commands/findings.js +329 -0
- package/dist/commands/findings.js.map +1 -0
- package/dist/commands/gateway.d.ts +62 -0
- package/dist/commands/gateway.d.ts.map +1 -0
- package/dist/commands/gateway.js +298 -0
- package/dist/commands/gateway.js.map +1 -0
- package/dist/commands/guardrails.d.ts +81 -0
- package/dist/commands/guardrails.d.ts.map +1 -0
- package/dist/commands/guardrails.js +735 -0
- package/dist/commands/guardrails.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +14 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install-daemon.d.ts +70 -3
- package/dist/commands/install-daemon.d.ts.map +1 -1
- package/dist/commands/install-daemon.js +482 -47
- package/dist/commands/install-daemon.js.map +1 -1
- package/dist/commands/integrity.d.ts +205 -0
- package/dist/commands/integrity.d.ts.map +1 -0
- package/dist/commands/integrity.js +620 -0
- package/dist/commands/integrity.js.map +1 -0
- package/dist/commands/judge.d.ts +31 -0
- package/dist/commands/judge.d.ts.map +1 -0
- package/dist/commands/judge.js +118 -0
- package/dist/commands/judge.js.map +1 -0
- package/dist/commands/org.d.ts +31 -0
- package/dist/commands/org.d.ts.map +1 -0
- package/dist/commands/org.js +159 -0
- package/dist/commands/org.js.map +1 -0
- package/dist/commands/policy.d.ts +138 -4
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +538 -97
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/predict-cost.d.ts +45 -0
- package/dist/commands/predict-cost.d.ts.map +1 -0
- package/dist/commands/predict-cost.js +123 -0
- package/dist/commands/predict-cost.js.map +1 -0
- package/dist/commands/rollback.d.ts +5 -0
- package/dist/commands/rollback.d.ts.map +1 -0
- package/dist/commands/rollback.js +142 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/routing.d.ts +62 -0
- package/dist/commands/routing.d.ts.map +1 -0
- package/dist/commands/routing.js +121 -0
- package/dist/commands/routing.js.map +1 -0
- package/dist/commands/setup.d.ts +62 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +238 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/skill.d.ts +268 -1
- package/dist/commands/skill.d.ts.map +1 -1
- package/dist/commands/skill.js +744 -39
- package/dist/commands/skill.js.map +1 -1
- package/dist/commands/sops.d.ts +95 -0
- package/dist/commands/sops.d.ts.map +1 -1
- package/dist/commands/sops.js +334 -34
- package/dist/commands/sops.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +1 -18
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +44 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/team.d.ts +38 -0
- package/dist/commands/team.d.ts.map +1 -0
- package/dist/commands/team.js +140 -0
- package/dist/commands/team.js.map +1 -0
- package/dist/commands/traces.d.ts +8 -1
- package/dist/commands/traces.d.ts.map +1 -1
- package/dist/commands/traces.js +114 -15
- package/dist/commands/traces.js.map +1 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +2 -1
- package/dist/commands/whoami.js.map +1 -1
- package/dist/config/keychain.d.ts +10 -2
- package/dist/config/keychain.d.ts.map +1 -1
- package/dist/config/keychain.js +83 -14
- package/dist/config/keychain.js.map +1 -1
- package/dist/config/paths.d.ts +27 -0
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +37 -6
- package/dist/config/paths.js.map +1 -1
- package/dist/config/store.d.ts.map +1 -1
- package/dist/config/store.js +19 -3
- package/dist/config/store.js.map +1 -1
- package/dist/harness/ag2.d.ts +22 -0
- package/dist/harness/ag2.d.ts.map +1 -0
- package/dist/harness/ag2.js +34 -0
- package/dist/harness/ag2.js.map +1 -0
- package/dist/harness/agentcore.d.ts +55 -0
- package/dist/harness/agentcore.d.ts.map +1 -0
- package/dist/harness/agentcore.js +119 -0
- package/dist/harness/agentcore.js.map +1 -0
- package/dist/harness/agenticOrchestrator.d.ts +52 -0
- package/dist/harness/agenticOrchestrator.d.ts.map +1 -0
- package/dist/harness/agenticOrchestrator.js +115 -0
- package/dist/harness/agenticOrchestrator.js.map +1 -0
- package/dist/harness/aiSdkHarness.d.ts +30 -0
- package/dist/harness/aiSdkHarness.d.ts.map +1 -0
- package/dist/harness/aiSdkHarness.js +51 -0
- package/dist/harness/aiSdkHarness.js.map +1 -0
- package/dist/harness/aiSdkWorkflow.d.ts +26 -0
- package/dist/harness/aiSdkWorkflow.d.ts.map +1 -0
- package/dist/harness/aiSdkWorkflow.js +39 -0
- package/dist/harness/aiSdkWorkflow.js.map +1 -0
- package/dist/harness/autogen.d.ts +24 -0
- package/dist/harness/autogen.d.ts.map +1 -0
- package/dist/harness/autogen.js +36 -0
- package/dist/harness/autogen.js.map +1 -0
- package/dist/harness/base.js.map +1 -1
- package/dist/harness/continue.d.ts.map +1 -1
- package/dist/harness/continue.js +2 -1
- package/dist/harness/continue.js.map +1 -1
- package/dist/harness/crewai.d.ts +15 -0
- package/dist/harness/crewai.d.ts.map +1 -0
- package/dist/harness/crewai.js +26 -0
- package/dist/harness/crewai.js.map +1 -0
- package/dist/harness/detector.d.ts +1 -1
- package/dist/harness/detector.d.ts.map +1 -1
- package/dist/harness/detector.js +47 -1
- package/dist/harness/detector.js.map +1 -1
- package/dist/harness/dsh.d.ts +19 -0
- package/dist/harness/dsh.d.ts.map +1 -0
- package/dist/harness/dsh.js +93 -0
- package/dist/harness/dsh.js.map +1 -0
- package/dist/harness/eve.d.ts +40 -0
- package/dist/harness/eve.d.ts.map +1 -0
- package/dist/harness/eve.js +67 -0
- package/dist/harness/eve.js.map +1 -0
- package/dist/harness/googleAdk.d.ts +18 -0
- package/dist/harness/googleAdk.d.ts.map +1 -0
- package/dist/harness/googleAdk.js +29 -0
- package/dist/harness/googleAdk.js.map +1 -0
- package/dist/harness/goose.d.ts.map +1 -1
- package/dist/harness/goose.js +5 -1
- package/dist/harness/goose.js.map +1 -1
- package/dist/harness/grok.d.ts +16 -0
- package/dist/harness/grok.d.ts.map +1 -0
- package/dist/harness/grok.js +78 -0
- package/dist/harness/grok.js.map +1 -0
- package/dist/harness/jsSdkGatedAdapter.d.ts +102 -0
- package/dist/harness/jsSdkGatedAdapter.d.ts.map +1 -0
- package/dist/harness/jsSdkGatedAdapter.js +139 -0
- package/dist/harness/jsSdkGatedAdapter.js.map +1 -0
- package/dist/harness/langchain.d.ts +22 -0
- package/dist/harness/langchain.d.ts.map +1 -0
- package/dist/harness/langchain.js +33 -0
- package/dist/harness/langchain.js.map +1 -0
- package/dist/harness/langgraph.d.ts +22 -0
- package/dist/harness/langgraph.d.ts.map +1 -0
- package/dist/harness/langgraph.js +80 -0
- package/dist/harness/langgraph.js.map +1 -0
- package/dist/harness/mastra.d.ts +21 -0
- package/dist/harness/mastra.d.ts.map +1 -0
- package/dist/harness/mastra.js +33 -0
- package/dist/harness/mastra.js.map +1 -0
- package/dist/harness/muse.d.ts +22 -0
- package/dist/harness/muse.d.ts.map +1 -0
- package/dist/harness/muse.js +92 -0
- package/dist/harness/muse.js.map +1 -0
- package/dist/harness/n8n.d.ts.map +1 -1
- package/dist/harness/n8n.js +36 -11
- package/dist/harness/n8n.js.map +1 -1
- package/dist/harness/openaiAgents.d.ts +40 -0
- package/dist/harness/openaiAgents.d.ts.map +1 -0
- package/dist/harness/openaiAgents.js +107 -0
- package/dist/harness/openaiAgents.js.map +1 -0
- package/dist/harness/opencode.d.ts +24 -0
- package/dist/harness/opencode.d.ts.map +1 -0
- package/dist/harness/opencode.js +97 -0
- package/dist/harness/opencode.js.map +1 -0
- package/dist/harness/pydanticAi.d.ts +19 -0
- package/dist/harness/pydanticAi.d.ts.map +1 -0
- package/dist/harness/pydanticAi.js +30 -0
- package/dist/harness/pydanticAi.js.map +1 -0
- package/dist/harness/sdkGatedAdapter.d.ts +55 -0
- package/dist/harness/sdkGatedAdapter.d.ts.map +1 -0
- package/dist/harness/sdkGatedAdapter.js +85 -0
- package/dist/harness/sdkGatedAdapter.js.map +1 -0
- package/dist/harness/smolagents.d.ts +22 -0
- package/dist/harness/smolagents.d.ts.map +1 -0
- package/dist/harness/smolagents.js +33 -0
- package/dist/harness/smolagents.js.map +1 -0
- package/dist/harness/strands.d.ts +22 -0
- package/dist/harness/strands.d.ts.map +1 -0
- package/dist/harness/strands.js +35 -0
- package/dist/harness/strands.js.map +1 -0
- package/dist/harness/trueforge.d.ts +25 -0
- package/dist/harness/trueforge.d.ts.map +1 -0
- package/dist/harness/trueforge.js +40 -0
- package/dist/harness/trueforge.js.map +1 -0
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js +70 -0
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/vercelAiSdk.d.ts +31 -0
- package/dist/harness/vercelAiSdk.d.ts.map +1 -0
- package/dist/harness/vercelAiSdk.js +44 -0
- package/dist/harness/vercelAiSdk.js.map +1 -0
- package/dist/harness/xirp.d.ts +43 -0
- package/dist/harness/xirp.d.ts.map +1 -0
- package/dist/harness/xirp.js +95 -0
- package/dist/harness/xirp.js.map +1 -0
- package/dist/lib/api.d.ts +68 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +108 -67
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/authMessages.d.ts +19 -0
- package/dist/lib/authMessages.d.ts.map +1 -0
- package/dist/lib/authMessages.js +20 -0
- package/dist/lib/authMessages.js.map +1 -0
- package/dist/lib/binaryChecksum.d.ts +12 -0
- package/dist/lib/binaryChecksum.d.ts.map +1 -0
- package/dist/lib/binaryChecksum.js +45 -0
- package/dist/lib/binaryChecksum.js.map +1 -0
- package/dist/lib/binaryOnPath.d.ts +24 -0
- package/dist/lib/binaryOnPath.d.ts.map +1 -0
- package/dist/lib/binaryOnPath.js +31 -0
- package/dist/lib/binaryOnPath.js.map +1 -0
- package/dist/lib/caTrust.d.ts +69 -0
- package/dist/lib/caTrust.d.ts.map +1 -0
- package/dist/lib/caTrust.js +169 -0
- package/dist/lib/caTrust.js.map +1 -0
- package/dist/lib/ciscoScanner.d.ts +178 -0
- package/dist/lib/ciscoScanner.d.ts.map +1 -0
- package/dist/lib/ciscoScanner.js +292 -0
- package/dist/lib/ciscoScanner.js.map +1 -0
- package/dist/lib/decisionsLogRefresh.d.ts +28 -0
- package/dist/lib/decisionsLogRefresh.d.ts.map +1 -0
- package/dist/lib/decisionsLogRefresh.js +54 -0
- package/dist/lib/decisionsLogRefresh.js.map +1 -0
- package/dist/lib/deviceReport.d.ts +26 -0
- package/dist/lib/deviceReport.d.ts.map +1 -0
- package/dist/lib/deviceReport.js +55 -0
- package/dist/lib/deviceReport.js.map +1 -0
- package/dist/lib/elevation.d.ts +58 -0
- package/dist/lib/elevation.d.ts.map +1 -0
- package/dist/lib/elevation.js +83 -0
- package/dist/lib/elevation.js.map +1 -0
- package/dist/lib/enforcementState.d.ts +69 -0
- package/dist/lib/enforcementState.d.ts.map +1 -0
- package/dist/lib/enforcementState.js +113 -0
- package/dist/lib/enforcementState.js.map +1 -0
- package/dist/lib/ensureValkey.d.ts.map +1 -1
- package/dist/lib/ensureValkey.js +2 -2
- package/dist/lib/ensureValkey.js.map +1 -1
- package/dist/lib/envInjector.d.ts.map +1 -1
- package/dist/lib/envInjector.js +22 -4
- package/dist/lib/envInjector.js.map +1 -1
- package/dist/lib/gitHooks.d.ts +7 -2
- package/dist/lib/gitHooks.d.ts.map +1 -1
- package/dist/lib/gitHooks.js +180 -13
- package/dist/lib/gitHooks.js.map +1 -1
- package/dist/lib/gracefulShutdown.d.ts +37 -0
- package/dist/lib/gracefulShutdown.d.ts.map +1 -0
- package/dist/lib/gracefulShutdown.js +57 -0
- package/dist/lib/gracefulShutdown.js.map +1 -0
- package/dist/lib/graphIdentity.d.ts +80 -0
- package/dist/lib/graphIdentity.d.ts.map +1 -0
- package/dist/lib/graphIdentity.js +120 -0
- package/dist/lib/graphIdentity.js.map +1 -0
- package/dist/lib/localTraces.d.ts +61 -0
- package/dist/lib/localTraces.d.ts.map +1 -0
- package/dist/lib/localTraces.js +173 -0
- package/dist/lib/localTraces.js.map +1 -0
- package/dist/lib/mdmManifest.d.ts +73 -0
- package/dist/lib/mdmManifest.d.ts.map +1 -0
- package/dist/lib/mdmManifest.js +190 -0
- package/dist/lib/mdmManifest.js.map +1 -0
- package/dist/lib/onboarding.d.ts.map +1 -1
- package/dist/lib/onboarding.js +60 -1
- package/dist/lib/onboarding.js.map +1 -1
- package/dist/lib/otelPropagation.d.ts +26 -0
- package/dist/lib/otelPropagation.d.ts.map +1 -0
- package/dist/lib/otelPropagation.js +40 -0
- package/dist/lib/otelPropagation.js.map +1 -0
- package/dist/lib/policySnapshot.d.ts +92 -0
- package/dist/lib/policySnapshot.d.ts.map +1 -0
- package/dist/lib/policySnapshot.js +173 -0
- package/dist/lib/policySnapshot.js.map +1 -0
- package/dist/lib/process.d.ts.map +1 -1
- package/dist/lib/process.js +60 -0
- package/dist/lib/process.js.map +1 -1
- package/dist/lib/providerProbe.d.ts +28 -0
- package/dist/lib/providerProbe.d.ts.map +1 -0
- package/dist/lib/providerProbe.js +57 -0
- package/dist/lib/providerProbe.js.map +1 -0
- package/dist/lib/proxyBinary.d.ts +19 -0
- package/dist/lib/proxyBinary.d.ts.map +1 -0
- package/dist/lib/proxyBinary.js +30 -0
- package/dist/lib/proxyBinary.js.map +1 -0
- package/dist/lib/sandbox/firecracker.d.ts +89 -0
- package/dist/lib/sandbox/firecracker.d.ts.map +1 -0
- package/dist/lib/sandbox/firecracker.js +135 -0
- package/dist/lib/sandbox/firecracker.js.map +1 -0
- package/dist/lib/sandbox/index.d.ts +34 -0
- package/dist/lib/sandbox/index.d.ts.map +1 -0
- package/dist/lib/sandbox/index.js +54 -0
- package/dist/lib/sandbox/index.js.map +1 -0
- package/dist/lib/sandbox/oci.d.ts +38 -0
- package/dist/lib/sandbox/oci.d.ts.map +1 -0
- package/dist/lib/sandbox/oci.js +108 -0
- package/dist/lib/sandbox/oci.js.map +1 -0
- package/dist/lib/sandbox/types.d.ts +48 -0
- package/dist/lib/sandbox/types.d.ts.map +1 -0
- package/dist/lib/sandbox/types.js +12 -0
- package/dist/lib/sandbox/types.js.map +1 -0
- package/dist/lib/sopFrontMatter.d.ts +50 -0
- package/dist/lib/sopFrontMatter.d.ts.map +1 -0
- package/dist/lib/sopFrontMatter.js +129 -0
- package/dist/lib/sopFrontMatter.js.map +1 -0
- package/package.json +11 -6
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OS trust-store injection for the Intutic proxy CA certificate.
|
|
3
|
+
*
|
|
4
|
+
* Two trust scopes:
|
|
5
|
+
* - `'user'` — per-user, non-elevated. Already shipping today via
|
|
6
|
+
* `commands/connect.ts` (macOS login keychain, Windows `certutil`), run
|
|
7
|
+
* silently on every `intutic connect`.
|
|
8
|
+
* - `'system'` — machine-wide, requires root/admin. New here — this is the
|
|
9
|
+
* half `enterprise install` (`commands/enterprise.ts`) needs and the
|
|
10
|
+
* deleted `enterprise-install.ts` (commit `8481b5d9`) used to provide.
|
|
11
|
+
*
|
|
12
|
+
* `caTrustCommandFor` is exported specifically so `doctor.ts`'s remediation
|
|
13
|
+
* strings can be generated from the same source as what actually runs here,
|
|
14
|
+
* rather than hand-duplicated — which is exactly how they already drifted
|
|
15
|
+
* once (the check there looks in three Linux cert-anchor directories; the
|
|
16
|
+
* remediation only ever names one).
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
import { execFile } from 'node:child_process';
|
|
21
|
+
import { promisify } from 'node:util';
|
|
22
|
+
import { copyFile, readFile } from 'node:fs/promises';
|
|
23
|
+
const execFileAsync = promisify(execFile);
|
|
24
|
+
const LINUX_DEBIAN_DEST = '/usr/local/share/ca-certificates/intutic-ca.crt';
|
|
25
|
+
const LINUX_RHEL_DEST = '/etc/pki/ca-trust/source/anchors/intutic-ca.crt';
|
|
26
|
+
/**
|
|
27
|
+
* The command(s) that trust `caCertPath` at the given scope, for the given
|
|
28
|
+
* platform. Pure — no I/O — so it doubles as the source of truth for
|
|
29
|
+
* `doctor.ts`'s remediation text.
|
|
30
|
+
*
|
|
31
|
+
* Linux `'user'` scope returns `null`: there is no single per-user NSS/OS
|
|
32
|
+
* trust-store equivalent worth building (each browser/runtime keeps its
|
|
33
|
+
* own), unlike macOS (login keychain) and Windows (`certutil`, which is
|
|
34
|
+
* elevation-agnostic — the same command works either way).
|
|
35
|
+
*
|
|
36
|
+
* Linux returns TWO commands (copy, then update) rather than one, since
|
|
37
|
+
* `execFile` can't run a shell pipeline — callers must run both in order.
|
|
38
|
+
*/
|
|
39
|
+
export function caTrustCommandFor(platform, scope, caCertPath) {
|
|
40
|
+
if (platform === 'darwin') {
|
|
41
|
+
const keychain = scope === 'system' ? '/Library/Keychains/System.keychain' : '~/Library/Keychains/login.keychain-db';
|
|
42
|
+
return [{
|
|
43
|
+
step: `Trust the CA cert in the macOS ${scope === 'system' ? 'System' : 'login'} keychain`,
|
|
44
|
+
cmd: 'security',
|
|
45
|
+
args: ['add-trusted-cert', '-d', '-r', 'trustRoot', '-k', keychain, caCertPath],
|
|
46
|
+
}];
|
|
47
|
+
}
|
|
48
|
+
if (platform === 'win32') {
|
|
49
|
+
return [{
|
|
50
|
+
step: 'Trust the CA cert in the Windows Root certificate store',
|
|
51
|
+
cmd: 'certutil',
|
|
52
|
+
args: ['-addstore', 'Root', caCertPath],
|
|
53
|
+
}];
|
|
54
|
+
}
|
|
55
|
+
if (platform === 'linux') {
|
|
56
|
+
if (scope === 'user')
|
|
57
|
+
return null;
|
|
58
|
+
// Debian/Ubuntu style is tried first by installCaTrust (most common);
|
|
59
|
+
// this function returns both possibilities so a caller (or doctor.ts)
|
|
60
|
+
// can describe either without probing which tool is actually present.
|
|
61
|
+
return [
|
|
62
|
+
{ step: 'Copy the CA cert into the system trust anchor directory (Debian/Ubuntu)', cmd: 'cp', args: [caCertPath, LINUX_DEBIAN_DEST] },
|
|
63
|
+
{ step: 'Rebuild the system CA bundle', cmd: 'update-ca-certificates', args: [] },
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
async function commandExists(cmd) {
|
|
69
|
+
try {
|
|
70
|
+
await execFileAsync('which', [cmd]);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function installLinuxSystemCaTrust(caCertPath) {
|
|
78
|
+
// Debian/Ubuntu is tried first (more common in practice); RHEL/Fedora's
|
|
79
|
+
// update-ca-trust is the fallback. Neither present -> fail loudly rather
|
|
80
|
+
// than silently doing nothing, matching this function's contract that a
|
|
81
|
+
// false `installed` always carries the real reason in `detail`.
|
|
82
|
+
if (await commandExists('update-ca-certificates')) {
|
|
83
|
+
await copyFile(caCertPath, LINUX_DEBIAN_DEST);
|
|
84
|
+
await execFileAsync('update-ca-certificates', []);
|
|
85
|
+
return { installed: true, scope: 'system', mechanism: 'update-ca-certificates', detail: `Installed to ${LINUX_DEBIAN_DEST}` };
|
|
86
|
+
}
|
|
87
|
+
if (await commandExists('update-ca-trust')) {
|
|
88
|
+
await copyFile(caCertPath, LINUX_RHEL_DEST);
|
|
89
|
+
await execFileAsync('update-ca-trust', ['extract']);
|
|
90
|
+
return { installed: true, scope: 'system', mechanism: 'update-ca-trust', detail: `Installed to ${LINUX_RHEL_DEST}` };
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
installed: false,
|
|
94
|
+
scope: 'system',
|
|
95
|
+
mechanism: 'none',
|
|
96
|
+
detail: 'Neither update-ca-certificates (Debian/Ubuntu) nor update-ca-trust (RHEL/Fedora) is available on this system.',
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Installs `caCertPath` into the OS trust store at `scope`.
|
|
101
|
+
*
|
|
102
|
+
* Uses `execFile` with array arguments throughout — never `execSync` with
|
|
103
|
+
* string interpolation, which `connect.ts`'s existing per-user calls use and
|
|
104
|
+
* which is unsafe if a path ever contained a shell metacharacter. Does not
|
|
105
|
+
* throw on a failed underlying command; surfaces it as `installed: false`
|
|
106
|
+
* with the OS error text in `detail`, so a caller (e.g. `enterprise install`)
|
|
107
|
+
* can report it and continue rather than crashing the whole run over one
|
|
108
|
+
* platform-specific step.
|
|
109
|
+
*/
|
|
110
|
+
export async function installCaTrust(caCertPath, scope) {
|
|
111
|
+
const platform = process.platform;
|
|
112
|
+
if (platform === 'linux' && scope === 'system') {
|
|
113
|
+
try {
|
|
114
|
+
return await installLinuxSystemCaTrust(caCertPath);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
return { installed: false, scope, mechanism: 'linux', detail: err instanceof Error ? err.message : String(err) };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const commands = caTrustCommandFor(platform, scope, caCertPath);
|
|
121
|
+
if (!commands) {
|
|
122
|
+
return {
|
|
123
|
+
installed: false,
|
|
124
|
+
scope,
|
|
125
|
+
mechanism: 'none',
|
|
126
|
+
detail: `No ${scope}-scope CA trust mechanism is implemented for ${platform}.`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
for (const c of commands) {
|
|
131
|
+
await execFileAsync(c.cmd, c.args);
|
|
132
|
+
}
|
|
133
|
+
const mechanism = platform === 'darwin' ? 'security' : platform === 'win32' ? 'certutil' : commands[commands.length - 1].cmd;
|
|
134
|
+
return { installed: true, scope, mechanism, detail: `Trusted via ${mechanism}` };
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
return { installed: false, scope, mechanism: platform, detail: err instanceof Error ? err.message : String(err) };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Best-effort check for whether `caCertPath` is already trusted — read-only,
|
|
142
|
+
* never installs anything. Used to skip a redundant (and log-noisy)
|
|
143
|
+
* re-install. Only implemented where a cheap, real check exists (macOS
|
|
144
|
+
* `security verify-cert`); returns `false` elsewhere rather than guessing,
|
|
145
|
+
* so a caller falls through to attempting installation, which is itself
|
|
146
|
+
* idempotent.
|
|
147
|
+
*/
|
|
148
|
+
export async function verifyCaTrust(caCertPath) {
|
|
149
|
+
if (process.platform === 'darwin') {
|
|
150
|
+
try {
|
|
151
|
+
await execFileAsync('security', ['verify-cert', '-c', caCertPath]);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (process.platform === 'linux') {
|
|
159
|
+
try {
|
|
160
|
+
const installed = await readFile(LINUX_DEBIAN_DEST, 'utf-8').catch(() => readFile(LINUX_RHEL_DEST, 'utf-8'));
|
|
161
|
+
return installed.length > 0;
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=caTrust.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caTrust.js","sourceRoot":"","sources":["../../src/lib/caTrust.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cisco `skill-scanner` integration — OPTIONAL, opt-in shell-out to
|
|
3
|
+
* `cisco-ai-defense/skill-scanner`, a Cisco AI Defense open-source CLI that
|
|
4
|
+
* does static YARA/pattern detection, Python bytecode integrity checks,
|
|
5
|
+
* command-pipeline taint analysis, and (with `--use-behavioral`) full
|
|
6
|
+
* AST/dataflow analysis of skill content — genuinely deeper than this
|
|
7
|
+
* codebase's own regex-genre scanning (`packages/shared-types/src/scriptScan.ts`,
|
|
8
|
+
* Phase S2). TD-356 (`docs/TECH_DEBT.md`) named this integration as the
|
|
9
|
+
* follow-up to that phase's deliberately-scoped pattern matching.
|
|
10
|
+
*
|
|
11
|
+
* # Why a shell-out, not a dependency
|
|
12
|
+
*
|
|
13
|
+
* `apps/docs/guide/skill-scanning.md` previously declined adopting a Python
|
|
14
|
+
* runtime dependency for this codebase — that decision stands. This module
|
|
15
|
+
* does not embed, vendor, or depend on `skill-scanner`; it shells out to it
|
|
16
|
+
* via `execFile` when the operator has separately installed it (`pipx
|
|
17
|
+
* install cisco-ai-skill-scanner`), and degrades gracefully — never throws,
|
|
18
|
+
* never silently passes — when it is absent. OUR runtime stays Python-free;
|
|
19
|
+
* the binary is entirely the operator's own installation.
|
|
20
|
+
*
|
|
21
|
+
* # CLI invocation shape — VERIFIED against the real published tool
|
|
22
|
+
*
|
|
23
|
+
* This integration was written against the actual Cisco tool: PyPI package
|
|
24
|
+
* `cisco-ai-skill-scanner` (installs a binary named `skill-scanner` on
|
|
25
|
+
* PATH — same binary name as a DIFFERENT, unrelated PyPI package also
|
|
26
|
+
* called `skill-scanner`; see the correction note below), installed via
|
|
27
|
+
* `pipx install cisco-ai-skill-scanner` and inspected with `--help` and by
|
|
28
|
+
* reading its installed source, not guessed from a "typical SARIF scanner"
|
|
29
|
+
* shape:
|
|
30
|
+
*
|
|
31
|
+
* skill-scanner scan <dir> --use-behavioral --format sarif --output-sarif <file>
|
|
32
|
+
*
|
|
33
|
+
* Load-bearing details confirmed from the installed 2.0.13 source
|
|
34
|
+
* (`skill_scanner/core/models.py`, `skill_scanner/cli/cli.py`) and by
|
|
35
|
+
* actually running the binary against test fixtures:
|
|
36
|
+
* - `skill_directory` is a POSITIONAL argument, not a `--path` flag, and
|
|
37
|
+
* takes exactly one directory — {@link runCiscoScan} therefore invokes
|
|
38
|
+
* the binary ONCE PER skill directory, exactly the shape this phase's
|
|
39
|
+
* caller (`commands/skill.ts`) already iterates in.
|
|
40
|
+
* - `static_analyzer`, `bytecode_analyzer`, and `pipeline_analyzer` run BY
|
|
41
|
+
* DEFAULT, no flag or API key required. `--use-behavioral` additionally
|
|
42
|
+
* enables `behavioral_analyzer` (AST + taint tracking) — still fully
|
|
43
|
+
* offline, no key — which is the analyzer this integration wants for the
|
|
44
|
+
* "genuinely deeper than regex" claim, so it is always passed.
|
|
45
|
+
* `--use-llm`, `--use-virustotal`, and `--use-aidefense` each require a
|
|
46
|
+
* separately-configured API key and are NEVER passed by this module —
|
|
47
|
+
* this integration only exercises skill-scanner's offline analyzers.
|
|
48
|
+
* - Each SARIF `result` carries `properties.category` (one of an 18-value
|
|
49
|
+
* enum — `prompt_injection`, `command_injection`, `data_exfiltration`,
|
|
50
|
+
* `unauthorized_tool_use`, `obfuscation`, `hardcoded_secrets`,
|
|
51
|
+
* `social_engineering`, `resource_abuse`, `policy_violation`, `malware`,
|
|
52
|
+
* `harmful_content`, `skill_discovery_abuse`, `transitive_trust_abuse`,
|
|
53
|
+
* `autonomy_abuse`, `tool_chaining_abuse`, `unicode_steganography`,
|
|
54
|
+
* `supply_chain_attack` — `skill_scanner/core/models.py`'s
|
|
55
|
+
* `ThreatCategory` enum) and `properties.severity` DIRECTLY, not encoded
|
|
56
|
+
* into `ruleId` — `ruleId` is a plain identifier like
|
|
57
|
+
* `PIPELINE_TAINT_FLOW`, not `skill-scanner/<category>`. `mapCiscoCategory`
|
|
58
|
+
* in `commands/skill.ts` maps from this real taxonomy, not a guess.
|
|
59
|
+
* - `artifactLocation.uri` is RELATIVE to the scanned directory (e.g.
|
|
60
|
+
* `"SKILL.md"`, with `uriBaseId: "%SRCROOT%"` signalling exactly that) —
|
|
61
|
+
* NOT absolute, confirmed by actually running the binary. {@link
|
|
62
|
+
* scanOneDir} joins it against `dir` before returning it, honoring this
|
|
63
|
+
* module's own `CiscoScanFinding.filePath` contract ("ABSOLUTE").
|
|
64
|
+
* - A `--fail-on-severity`/`--fail-on-findings` threshold crossing still
|
|
65
|
+
* exits non-zero WHILE writing a valid SARIF file — confirmed
|
|
66
|
+
* empirically — so {@link scanOneDir} always attempts to read the output
|
|
67
|
+
* file first and only reports `ok: false` when that file is absent or
|
|
68
|
+
* unparseable.
|
|
69
|
+
*
|
|
70
|
+
* # Correction: an earlier version of this integration shelled out to the
|
|
71
|
+
* # WRONG package
|
|
72
|
+
*
|
|
73
|
+
* `skill-scanner` is claimed on PyPI's generic namespace by an unrelated,
|
|
74
|
+
* unaffiliated project (`thedevappsecguy/skill-scanner`, MIT-licensed, no
|
|
75
|
+
* Cisco affiliation) that requires an LLM or VirusTotal API key to do
|
|
76
|
+
* anything at all — it has no offline analysis path. `pipx install
|
|
77
|
+
* skill-scanner` installs THAT package, not Cisco's. The real Cisco tool
|
|
78
|
+
* publishes under `cisco-ai-skill-scanner` on PyPI
|
|
79
|
+
* (github.com/cisco-ai-defense/skill-scanner, Apache 2.0, Cisco Systems
|
|
80
|
+
* Inc.) — install it with `pipx install cisco-ai-skill-scanner`. Once
|
|
81
|
+
* installed, its binary is ALSO named `skill-scanner` on PATH, so no other
|
|
82
|
+
* part of this integration (binary-name detection, doctor check) needed to
|
|
83
|
+
* change — only the install instructions and this module's invocation
|
|
84
|
+
* shape/category taxonomy, which were verified against the wrong package.
|
|
85
|
+
*
|
|
86
|
+
* @module
|
|
87
|
+
*/
|
|
88
|
+
/** The binary name this integration shells out to. Matches the doctor-check
|
|
89
|
+
* remediation (`pipx install cisco-ai-skill-scanner`) — the PyPI package
|
|
90
|
+
* name differs from the binary name it installs; see this module's doc
|
|
91
|
+
* comment. */
|
|
92
|
+
export declare const CISCO_SCANNER_BINARY = "skill-scanner";
|
|
93
|
+
/**
|
|
94
|
+
* Bounded timeout per scanned directory. Generous relative to every other
|
|
95
|
+
* shell-out in this codebase (`elevation.ts`'s `net session` probe: 3s;
|
|
96
|
+
* `process.ts`'s `ps`/`tasklist`: 5s) because `--use-behavioral` runs a
|
|
97
|
+
* full AST/dataflow pass, not a fast pattern match — 60s is a floor meant
|
|
98
|
+
* to catch a genuinely hung process, not a tight budget for a fast local
|
|
99
|
+
* check.
|
|
100
|
+
*/
|
|
101
|
+
export declare const CISCO_SCAN_TIMEOUT_MS = 60000;
|
|
102
|
+
/** Resolves `true` iff the `skill-scanner` binary is present on PATH. */
|
|
103
|
+
export declare function ciscoScannerOnPath(): Promise<boolean>;
|
|
104
|
+
/**
|
|
105
|
+
* `skill-scanner --version`'s stdout, trimmed, or `null` if the binary is
|
|
106
|
+
* absent or the invocation itself fails. Used only by the doctor check —
|
|
107
|
+
* never assumed non-null by any caller that gates on {@link ciscoScannerOnPath}.
|
|
108
|
+
*/
|
|
109
|
+
export declare function ciscoScannerVersion(): Promise<string | null>;
|
|
110
|
+
/** One parsed SARIF result from a `skill-scanner` run, flattened out of
|
|
111
|
+
* `runs[].results` for the internal finding-merge path. Bounded fields
|
|
112
|
+
* only — never the tool's full result object — matching this codebase's
|
|
113
|
+
* own findings' "never carry more than a caller needs" discipline. */
|
|
114
|
+
export interface CiscoScanFinding {
|
|
115
|
+
/** The tool's own `ruleId`, e.g. `PIPELINE_TAINT_FLOW`. Not itself a
|
|
116
|
+
* category — see {@link category}. */
|
|
117
|
+
ruleId: string;
|
|
118
|
+
level: string;
|
|
119
|
+
message: string;
|
|
120
|
+
/** The tool's own `properties.category` on this result, e.g.
|
|
121
|
+
* `command_injection` — one of the `ThreatCategory` enum values
|
|
122
|
+
* documented in this module's doc comment. Absent only for a
|
|
123
|
+
* malformed/unexpected result shape. */
|
|
124
|
+
category?: string;
|
|
125
|
+
/** SARIF `artifactLocation.uri` — the tool's own path, ABSOLUTE (whatever
|
|
126
|
+
* form `runCiscoScan`'s caller passed as the scanned directory), not yet
|
|
127
|
+
* relativized to a workspace root. */
|
|
128
|
+
filePath?: string;
|
|
129
|
+
line?: number;
|
|
130
|
+
}
|
|
131
|
+
/** Result of scanning ONE directory with `skill-scanner`. */
|
|
132
|
+
export interface CiscoDirScanResult {
|
|
133
|
+
/** The directory that was scanned — echoes the caller's input. */
|
|
134
|
+
dir: string;
|
|
135
|
+
/** `false` means this directory produced no usable result — binary
|
|
136
|
+
* absent, timed out, or exited without a parseable SARIF file. Refusal-
|
|
137
|
+
* not-pass: never treat `!ok` as "clean". */
|
|
138
|
+
ok: boolean;
|
|
139
|
+
/** Populated only when `!ok`. Human-readable — safe to log or surface to
|
|
140
|
+
* the user directly. */
|
|
141
|
+
error?: string;
|
|
142
|
+
/** The RAW SARIF `runs[]` entries this invocation produced, exactly as
|
|
143
|
+
* the tool emitted them — for verbatim passthrough into `--sarif` output.
|
|
144
|
+
* Usually zero or one entry per directory; never edited or re-shaped. */
|
|
145
|
+
sarifRuns: unknown[];
|
|
146
|
+
/** Findings flattened out of `sarifRuns[].results`, for the internal
|
|
147
|
+
* `SkillScanFinding` merge path (`commands/skill.ts`). */
|
|
148
|
+
findings: CiscoScanFinding[];
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Scan each of `skillDirs` with `skill-scanner`, one invocation per
|
|
152
|
+
* directory (see this module's doc comment for why the tool takes exactly
|
|
153
|
+
* one directory per invocation). Directories are ABSOLUTE paths — callers
|
|
154
|
+
* resolve against the workspace root before calling, the same convention
|
|
155
|
+
* `oci.ts`/`firecracker.ts` use for the paths they hand external tools.
|
|
156
|
+
*
|
|
157
|
+
* Never throws: every failure mode (binary absent, timeout, unparseable
|
|
158
|
+
* output) is reported per-directory via `CiscoDirScanResult.ok`/`.error`,
|
|
159
|
+
* so one skill's scan failing does not abort the rest.
|
|
160
|
+
*/
|
|
161
|
+
export declare function runCiscoScan(skillDirs: readonly string[]): Promise<CiscoDirScanResult[]>;
|
|
162
|
+
/**
|
|
163
|
+
* Combine the raw SARIF `runs[0]`-shaped objects from multiple
|
|
164
|
+
* `skill-scanner` invocations (one per scanned skill directory) into a
|
|
165
|
+
* SINGLE run, so `skill audit --sarif` appends Cisco's output as one
|
|
166
|
+
* additional entry in `runs[]`, not one per skill scanned — the multiple
|
|
167
|
+
* invocations are an artifact of this integration's own per-directory
|
|
168
|
+
* invocation strategy, not something the SARIF consumer should see.
|
|
169
|
+
*
|
|
170
|
+
* Only the CONTAINER is merged: rule catalogs are de-duplicated by `id`,
|
|
171
|
+
* results are concatenated. Every individual rule/result object is passed
|
|
172
|
+
* through unmodified — never rewritten — preserving the "append verbatim"
|
|
173
|
+
* contract this integration promises for `--sarif` output.
|
|
174
|
+
*
|
|
175
|
+
* Returns `null` when given no runs (nothing to append).
|
|
176
|
+
*/
|
|
177
|
+
export declare function combineCiscoSarifRuns(runs: readonly unknown[]): unknown | null;
|
|
178
|
+
//# sourceMappingURL=ciscoScanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ciscoScanner.d.ts","sourceRoot":"","sources":["../../src/lib/ciscoScanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;AAYH;;;eAGe;AACf,eAAO,MAAM,oBAAoB,kBAAkB,CAAA;AAEnD;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,QAAS,CAAA;AAE3C,yEAAyE;AACzE,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CAErD;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQlE;AAED;;;uEAGuE;AACvE,MAAM,WAAW,gBAAgB;IAC/B;2CACuC;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf;;;6CAGyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;2CAEuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,6DAA6D;AAC7D,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAA;IACX;;kDAE8C;IAC9C,EAAE,EAAE,OAAO,CAAA;IACX;6BACyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;8EAE0E;IAC1E,SAAS,EAAE,OAAO,EAAE,CAAA;IACpB;+DAC2D;IAC3D,QAAQ,EAAE,gBAAgB,EAAE,CAAA;CAC7B;AAkID;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAM9F;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,GAAG,IAAI,CAyB9E"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cisco `skill-scanner` integration — OPTIONAL, opt-in shell-out to
|
|
3
|
+
* `cisco-ai-defense/skill-scanner`, a Cisco AI Defense open-source CLI that
|
|
4
|
+
* does static YARA/pattern detection, Python bytecode integrity checks,
|
|
5
|
+
* command-pipeline taint analysis, and (with `--use-behavioral`) full
|
|
6
|
+
* AST/dataflow analysis of skill content — genuinely deeper than this
|
|
7
|
+
* codebase's own regex-genre scanning (`packages/shared-types/src/scriptScan.ts`,
|
|
8
|
+
* Phase S2). TD-356 (`docs/TECH_DEBT.md`) named this integration as the
|
|
9
|
+
* follow-up to that phase's deliberately-scoped pattern matching.
|
|
10
|
+
*
|
|
11
|
+
* # Why a shell-out, not a dependency
|
|
12
|
+
*
|
|
13
|
+
* `apps/docs/guide/skill-scanning.md` previously declined adopting a Python
|
|
14
|
+
* runtime dependency for this codebase — that decision stands. This module
|
|
15
|
+
* does not embed, vendor, or depend on `skill-scanner`; it shells out to it
|
|
16
|
+
* via `execFile` when the operator has separately installed it (`pipx
|
|
17
|
+
* install cisco-ai-skill-scanner`), and degrades gracefully — never throws,
|
|
18
|
+
* never silently passes — when it is absent. OUR runtime stays Python-free;
|
|
19
|
+
* the binary is entirely the operator's own installation.
|
|
20
|
+
*
|
|
21
|
+
* # CLI invocation shape — VERIFIED against the real published tool
|
|
22
|
+
*
|
|
23
|
+
* This integration was written against the actual Cisco tool: PyPI package
|
|
24
|
+
* `cisco-ai-skill-scanner` (installs a binary named `skill-scanner` on
|
|
25
|
+
* PATH — same binary name as a DIFFERENT, unrelated PyPI package also
|
|
26
|
+
* called `skill-scanner`; see the correction note below), installed via
|
|
27
|
+
* `pipx install cisco-ai-skill-scanner` and inspected with `--help` and by
|
|
28
|
+
* reading its installed source, not guessed from a "typical SARIF scanner"
|
|
29
|
+
* shape:
|
|
30
|
+
*
|
|
31
|
+
* skill-scanner scan <dir> --use-behavioral --format sarif --output-sarif <file>
|
|
32
|
+
*
|
|
33
|
+
* Load-bearing details confirmed from the installed 2.0.13 source
|
|
34
|
+
* (`skill_scanner/core/models.py`, `skill_scanner/cli/cli.py`) and by
|
|
35
|
+
* actually running the binary against test fixtures:
|
|
36
|
+
* - `skill_directory` is a POSITIONAL argument, not a `--path` flag, and
|
|
37
|
+
* takes exactly one directory — {@link runCiscoScan} therefore invokes
|
|
38
|
+
* the binary ONCE PER skill directory, exactly the shape this phase's
|
|
39
|
+
* caller (`commands/skill.ts`) already iterates in.
|
|
40
|
+
* - `static_analyzer`, `bytecode_analyzer`, and `pipeline_analyzer` run BY
|
|
41
|
+
* DEFAULT, no flag or API key required. `--use-behavioral` additionally
|
|
42
|
+
* enables `behavioral_analyzer` (AST + taint tracking) — still fully
|
|
43
|
+
* offline, no key — which is the analyzer this integration wants for the
|
|
44
|
+
* "genuinely deeper than regex" claim, so it is always passed.
|
|
45
|
+
* `--use-llm`, `--use-virustotal`, and `--use-aidefense` each require a
|
|
46
|
+
* separately-configured API key and are NEVER passed by this module —
|
|
47
|
+
* this integration only exercises skill-scanner's offline analyzers.
|
|
48
|
+
* - Each SARIF `result` carries `properties.category` (one of an 18-value
|
|
49
|
+
* enum — `prompt_injection`, `command_injection`, `data_exfiltration`,
|
|
50
|
+
* `unauthorized_tool_use`, `obfuscation`, `hardcoded_secrets`,
|
|
51
|
+
* `social_engineering`, `resource_abuse`, `policy_violation`, `malware`,
|
|
52
|
+
* `harmful_content`, `skill_discovery_abuse`, `transitive_trust_abuse`,
|
|
53
|
+
* `autonomy_abuse`, `tool_chaining_abuse`, `unicode_steganography`,
|
|
54
|
+
* `supply_chain_attack` — `skill_scanner/core/models.py`'s
|
|
55
|
+
* `ThreatCategory` enum) and `properties.severity` DIRECTLY, not encoded
|
|
56
|
+
* into `ruleId` — `ruleId` is a plain identifier like
|
|
57
|
+
* `PIPELINE_TAINT_FLOW`, not `skill-scanner/<category>`. `mapCiscoCategory`
|
|
58
|
+
* in `commands/skill.ts` maps from this real taxonomy, not a guess.
|
|
59
|
+
* - `artifactLocation.uri` is RELATIVE to the scanned directory (e.g.
|
|
60
|
+
* `"SKILL.md"`, with `uriBaseId: "%SRCROOT%"` signalling exactly that) —
|
|
61
|
+
* NOT absolute, confirmed by actually running the binary. {@link
|
|
62
|
+
* scanOneDir} joins it against `dir` before returning it, honoring this
|
|
63
|
+
* module's own `CiscoScanFinding.filePath` contract ("ABSOLUTE").
|
|
64
|
+
* - A `--fail-on-severity`/`--fail-on-findings` threshold crossing still
|
|
65
|
+
* exits non-zero WHILE writing a valid SARIF file — confirmed
|
|
66
|
+
* empirically — so {@link scanOneDir} always attempts to read the output
|
|
67
|
+
* file first and only reports `ok: false` when that file is absent or
|
|
68
|
+
* unparseable.
|
|
69
|
+
*
|
|
70
|
+
* # Correction: an earlier version of this integration shelled out to the
|
|
71
|
+
* # WRONG package
|
|
72
|
+
*
|
|
73
|
+
* `skill-scanner` is claimed on PyPI's generic namespace by an unrelated,
|
|
74
|
+
* unaffiliated project (`thedevappsecguy/skill-scanner`, MIT-licensed, no
|
|
75
|
+
* Cisco affiliation) that requires an LLM or VirusTotal API key to do
|
|
76
|
+
* anything at all — it has no offline analysis path. `pipx install
|
|
77
|
+
* skill-scanner` installs THAT package, not Cisco's. The real Cisco tool
|
|
78
|
+
* publishes under `cisco-ai-skill-scanner` on PyPI
|
|
79
|
+
* (github.com/cisco-ai-defense/skill-scanner, Apache 2.0, Cisco Systems
|
|
80
|
+
* Inc.) — install it with `pipx install cisco-ai-skill-scanner`. Once
|
|
81
|
+
* installed, its binary is ALSO named `skill-scanner` on PATH, so no other
|
|
82
|
+
* part of this integration (binary-name detection, doctor check) needed to
|
|
83
|
+
* change — only the install instructions and this module's invocation
|
|
84
|
+
* shape/category taxonomy, which were verified against the wrong package.
|
|
85
|
+
*
|
|
86
|
+
* @module
|
|
87
|
+
*/
|
|
88
|
+
import { execFile } from 'node:child_process';
|
|
89
|
+
import { promisify } from 'node:util';
|
|
90
|
+
import * as fs from 'node:fs/promises';
|
|
91
|
+
import { tmpdir } from 'node:os';
|
|
92
|
+
import { isAbsolute, join } from 'node:path';
|
|
93
|
+
import { randomBytes } from 'node:crypto';
|
|
94
|
+
import { binaryOnPath } from './binaryOnPath.js';
|
|
95
|
+
const execFileAsync = promisify(execFile);
|
|
96
|
+
/** The binary name this integration shells out to. Matches the doctor-check
|
|
97
|
+
* remediation (`pipx install cisco-ai-skill-scanner`) — the PyPI package
|
|
98
|
+
* name differs from the binary name it installs; see this module's doc
|
|
99
|
+
* comment. */
|
|
100
|
+
export const CISCO_SCANNER_BINARY = 'skill-scanner';
|
|
101
|
+
/**
|
|
102
|
+
* Bounded timeout per scanned directory. Generous relative to every other
|
|
103
|
+
* shell-out in this codebase (`elevation.ts`'s `net session` probe: 3s;
|
|
104
|
+
* `process.ts`'s `ps`/`tasklist`: 5s) because `--use-behavioral` runs a
|
|
105
|
+
* full AST/dataflow pass, not a fast pattern match — 60s is a floor meant
|
|
106
|
+
* to catch a genuinely hung process, not a tight budget for a fast local
|
|
107
|
+
* check.
|
|
108
|
+
*/
|
|
109
|
+
export const CISCO_SCAN_TIMEOUT_MS = 60_000;
|
|
110
|
+
/** Resolves `true` iff the `skill-scanner` binary is present on PATH. */
|
|
111
|
+
export function ciscoScannerOnPath() {
|
|
112
|
+
return binaryOnPath(CISCO_SCANNER_BINARY);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* `skill-scanner --version`'s stdout, trimmed, or `null` if the binary is
|
|
116
|
+
* absent or the invocation itself fails. Used only by the doctor check —
|
|
117
|
+
* never assumed non-null by any caller that gates on {@link ciscoScannerOnPath}.
|
|
118
|
+
*/
|
|
119
|
+
export async function ciscoScannerVersion() {
|
|
120
|
+
try {
|
|
121
|
+
const { stdout } = await execFileAsync(CISCO_SCANNER_BINARY, ['--version'], { timeout: 5_000 });
|
|
122
|
+
const trimmed = stdout.trim();
|
|
123
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function friendlyMissingBinaryError() {
|
|
130
|
+
return (`'${CISCO_SCANNER_BINARY}' not found on PATH. Install it with ` +
|
|
131
|
+
'`pipx install cisco-ai-skill-scanner`, or drop `--engine cisco` / ' +
|
|
132
|
+
"disable the 'ciscoSkillScannerEnabled' workspace setting to use native " +
|
|
133
|
+
'scanning only.');
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Scan ONE directory with `skill-scanner`, writing SARIF to a scratch file
|
|
137
|
+
* (`--output-sarif`) rather than parsing stdout — stdout carries a
|
|
138
|
+
* human-readable summary this integration never needs to parse, and the
|
|
139
|
+
* SARIF exporter writes plain JSON to the file regardless of `--format`
|
|
140
|
+
* also being requested for stdout.
|
|
141
|
+
*
|
|
142
|
+
* A non-zero exit is NOT immediately treated as failure: `--fail-on-*`
|
|
143
|
+
* deliberately exits non-zero when a finding crosses a severity threshold
|
|
144
|
+
* while still writing a valid SARIF file (verified empirically), so this
|
|
145
|
+
* function always attempts to read the output file first and only reports
|
|
146
|
+
* `ok: false` when that file is absent or unparseable.
|
|
147
|
+
*/
|
|
148
|
+
async function scanOneDir(dir) {
|
|
149
|
+
const outFile = join(tmpdir(), `intutic-cisco-scan-${randomBytes(8).toString('hex')}.sarif.json`);
|
|
150
|
+
let execError;
|
|
151
|
+
try {
|
|
152
|
+
await execFileAsync(CISCO_SCANNER_BINARY, ['scan', dir, '--use-behavioral', '--format', 'sarif', '--output-sarif', outFile], { timeout: CISCO_SCAN_TIMEOUT_MS });
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
const nodeErr = err;
|
|
156
|
+
if (nodeErr.code === 'ENOENT') {
|
|
157
|
+
return { dir, ok: false, sarifRuns: [], findings: [], error: friendlyMissingBinaryError() };
|
|
158
|
+
}
|
|
159
|
+
if (nodeErr.killed || nodeErr.signal) {
|
|
160
|
+
return {
|
|
161
|
+
dir,
|
|
162
|
+
ok: false,
|
|
163
|
+
sarifRuns: [],
|
|
164
|
+
findings: [],
|
|
165
|
+
error: `cisco skill-scanner timed out after ${CISCO_SCAN_TIMEOUT_MS}ms scanning ${dir}`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// Any other non-zero exit — fall through and try the output file before
|
|
169
|
+
// giving up (see doc comment: --fail-on-* exits non-zero on real findings).
|
|
170
|
+
execError = nodeErr;
|
|
171
|
+
}
|
|
172
|
+
let raw;
|
|
173
|
+
try {
|
|
174
|
+
raw = await fs.readFile(outFile, 'utf8');
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
const stderrHint = execError?.stderr?.trim();
|
|
178
|
+
return {
|
|
179
|
+
dir,
|
|
180
|
+
ok: false,
|
|
181
|
+
sarifRuns: [],
|
|
182
|
+
findings: [],
|
|
183
|
+
error: `cisco skill-scanner produced no output scanning ${dir}` +
|
|
184
|
+
(stderrHint ? `: ${stderrHint}` : ' — the scan did not complete.'),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
await fs.rm(outFile, { force: true });
|
|
189
|
+
}
|
|
190
|
+
let parsed;
|
|
191
|
+
try {
|
|
192
|
+
parsed = JSON.parse(raw);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return {
|
|
196
|
+
dir,
|
|
197
|
+
ok: false,
|
|
198
|
+
sarifRuns: [],
|
|
199
|
+
findings: [],
|
|
200
|
+
error: `cisco skill-scanner output for ${dir} was not valid JSON`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const runs = Array.isArray(parsed.runs) ? parsed.runs : [];
|
|
204
|
+
const findings = [];
|
|
205
|
+
for (const run of runs) {
|
|
206
|
+
const results = run?.results;
|
|
207
|
+
if (!Array.isArray(results))
|
|
208
|
+
continue;
|
|
209
|
+
for (const result of results) {
|
|
210
|
+
const r = result;
|
|
211
|
+
const location = r.locations?.[0]?.physicalLocation;
|
|
212
|
+
const uri = typeof location?.artifactLocation?.uri === 'string' ? location.artifactLocation.uri : undefined;
|
|
213
|
+
// The tool's own URIs are RELATIVE to the scanned directory (verified
|
|
214
|
+
// empirically: `artifactLocation.uri` is `"SKILL.md"`, not an
|
|
215
|
+
// absolute path, with `uriBaseId: "%SRCROOT%"` signalling exactly
|
|
216
|
+
// that) — join against `dir` so this module's own contract
|
|
217
|
+
// ("filePath is ABSOLUTE") holds regardless of what the tool emits.
|
|
218
|
+
// Already-absolute URIs (a future tool version, or a result with no
|
|
219
|
+
// location at all) pass through unchanged.
|
|
220
|
+
const filePath = uri === undefined ? undefined : isAbsolute(uri) ? uri : join(dir, uri);
|
|
221
|
+
findings.push({
|
|
222
|
+
ruleId: typeof r.ruleId === 'string' ? r.ruleId : 'unknown',
|
|
223
|
+
level: typeof r.level === 'string' ? r.level : 'warning',
|
|
224
|
+
message: typeof r.message?.text === 'string' ? r.message.text : '',
|
|
225
|
+
category: typeof r.properties?.category === 'string' ? r.properties.category : undefined,
|
|
226
|
+
filePath,
|
|
227
|
+
line: typeof location?.region?.startLine === 'number' ? location.region.startLine : undefined,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return { dir, ok: true, sarifRuns: runs, findings };
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Scan each of `skillDirs` with `skill-scanner`, one invocation per
|
|
235
|
+
* directory (see this module's doc comment for why the tool takes exactly
|
|
236
|
+
* one directory per invocation). Directories are ABSOLUTE paths — callers
|
|
237
|
+
* resolve against the workspace root before calling, the same convention
|
|
238
|
+
* `oci.ts`/`firecracker.ts` use for the paths they hand external tools.
|
|
239
|
+
*
|
|
240
|
+
* Never throws: every failure mode (binary absent, timeout, unparseable
|
|
241
|
+
* output) is reported per-directory via `CiscoDirScanResult.ok`/`.error`,
|
|
242
|
+
* so one skill's scan failing does not abort the rest.
|
|
243
|
+
*/
|
|
244
|
+
export async function runCiscoScan(skillDirs) {
|
|
245
|
+
const results = [];
|
|
246
|
+
for (const dir of skillDirs) {
|
|
247
|
+
results.push(await scanOneDir(dir));
|
|
248
|
+
}
|
|
249
|
+
return results;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Combine the raw SARIF `runs[0]`-shaped objects from multiple
|
|
253
|
+
* `skill-scanner` invocations (one per scanned skill directory) into a
|
|
254
|
+
* SINGLE run, so `skill audit --sarif` appends Cisco's output as one
|
|
255
|
+
* additional entry in `runs[]`, not one per skill scanned — the multiple
|
|
256
|
+
* invocations are an artifact of this integration's own per-directory
|
|
257
|
+
* invocation strategy, not something the SARIF consumer should see.
|
|
258
|
+
*
|
|
259
|
+
* Only the CONTAINER is merged: rule catalogs are de-duplicated by `id`,
|
|
260
|
+
* results are concatenated. Every individual rule/result object is passed
|
|
261
|
+
* through unmodified — never rewritten — preserving the "append verbatim"
|
|
262
|
+
* contract this integration promises for `--sarif` output.
|
|
263
|
+
*
|
|
264
|
+
* Returns `null` when given no runs (nothing to append).
|
|
265
|
+
*/
|
|
266
|
+
export function combineCiscoSarifRuns(runs) {
|
|
267
|
+
if (runs.length === 0)
|
|
268
|
+
return null;
|
|
269
|
+
const base = runs[0];
|
|
270
|
+
const rulesById = new Map();
|
|
271
|
+
const results = [];
|
|
272
|
+
for (const run of runs) {
|
|
273
|
+
const r = run;
|
|
274
|
+
for (const rule of r.tool?.driver?.rules ?? []) {
|
|
275
|
+
const id = rule?.id;
|
|
276
|
+
if (typeof id === 'string')
|
|
277
|
+
rulesById.set(id, rule);
|
|
278
|
+
}
|
|
279
|
+
if (Array.isArray(r.results))
|
|
280
|
+
results.push(...r.results);
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
tool: {
|
|
284
|
+
driver: {
|
|
285
|
+
name: typeof base.tool?.driver?.name === 'string' ? base.tool.driver.name : CISCO_SCANNER_BINARY,
|
|
286
|
+
rules: [...rulesById.values()],
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
results,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=ciscoScanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ciscoScanner.js","sourceRoot":"","sources":["../../src/lib/ciscoScanner.ts"],"names":[],"mappings":"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"}
|