@kolatts/pncli 1.9.0 → 1.10.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/dist/{chunk-QQZJQ5DP.js → chunk-3XHTRFRX.js} +34 -1
- package/dist/chunk-3XHTRFRX.js.map +1 -0
- package/dist/{chunk-YR6GFJMV.js → chunk-O3LWAWSN.js} +13 -1
- package/dist/chunk-O3LWAWSN.js.map +1 -0
- package/dist/cli.js +427 -52
- package/dist/cli.js.map +1 -1
- package/dist/{config-3VKBM32K.js → config-NCI2BTQG.js} +2 -2
- package/dist/{http-GVHZQCEY.js → http-L2P23H2D.js} +2 -2
- package/package.json +2 -1
- package/skills/address-pr-feedback/SKILL.md +87 -0
- package/skills/code-review/SKILL.md +82 -0
- package/skills/local-setup/SKILL.md +225 -0
- package/skills/plan/SKILL.md +120 -0
- package/skills/security-review/SKILL.md +108 -0
- package/skills/ship/SKILL.md +170 -0
- package/dist/chunk-QQZJQ5DP.js.map +0 -1
- package/dist/chunk-YR6GFJMV.js.map +0 -1
- /package/dist/{config-3VKBM32K.js.map → config-NCI2BTQG.js.map} +0 -0
- /package/dist/{http-GVHZQCEY.js.map → http-L2P23H2D.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
setRepoConfigValue,
|
|
8
8
|
writeGlobalConfig,
|
|
9
9
|
writeRepoConfig
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-O3LWAWSN.js";
|
|
11
11
|
import {
|
|
12
12
|
ExitCode,
|
|
13
13
|
PncliError,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
setGlobalUser,
|
|
19
19
|
success,
|
|
20
20
|
warn
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-3XHTRFRX.js";
|
|
22
22
|
import {
|
|
23
23
|
AdoCoreClient
|
|
24
24
|
} from "./chunk-VDL32PSG.js";
|
|
@@ -3021,11 +3021,224 @@ async function checkPackagesForVulnsViaSonatype(packages) {
|
|
|
3021
3021
|
return allVulnerable;
|
|
3022
3022
|
}
|
|
3023
3023
|
|
|
3024
|
-
// src/services/deps/clients/
|
|
3025
|
-
var TIMEOUT_MS3 =
|
|
3026
|
-
|
|
3024
|
+
// src/services/deps/clients/sonatypeiq.ts
|
|
3025
|
+
var TIMEOUT_MS3 = 6e4;
|
|
3026
|
+
var MAX_POLL_ATTEMPTS = 10;
|
|
3027
|
+
var POLL_INTERVAL_MS = 2e3;
|
|
3028
|
+
var BATCH_SIZE3 = 128;
|
|
3029
|
+
async function fetchWithTimeout3(url, init) {
|
|
3027
3030
|
const controller = new AbortController();
|
|
3028
3031
|
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS3);
|
|
3032
|
+
try {
|
|
3033
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
3034
|
+
} finally {
|
|
3035
|
+
clearTimeout(timer);
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
function buildBasicAuth(userCode, passcode) {
|
|
3039
|
+
return `Basic ${Buffer.from(`${userCode}:${passcode}`).toString("base64")}`;
|
|
3040
|
+
}
|
|
3041
|
+
function threatLevelToSeverity(level) {
|
|
3042
|
+
if (level >= 9) return "CRITICAL";
|
|
3043
|
+
if (level >= 7) return "HIGH";
|
|
3044
|
+
if (level >= 4) return "MEDIUM";
|
|
3045
|
+
if (level > 0) return "LOW";
|
|
3046
|
+
return "UNKNOWN";
|
|
3047
|
+
}
|
|
3048
|
+
function toPurl2(pkg2) {
|
|
3049
|
+
switch (pkg2.ecosystem) {
|
|
3050
|
+
case "npm": {
|
|
3051
|
+
const encodedName = pkg2.name.startsWith("@") ? pkg2.name.replace("@", "%40") : pkg2.name;
|
|
3052
|
+
return `pkg:npm/${encodedName}@${pkg2.version}`;
|
|
3053
|
+
}
|
|
3054
|
+
case "nuget":
|
|
3055
|
+
return `pkg:nuget/${pkg2.name}@${pkg2.version}`;
|
|
3056
|
+
case "maven": {
|
|
3057
|
+
const colonIdx = pkg2.name.indexOf(":");
|
|
3058
|
+
if (colonIdx !== -1) {
|
|
3059
|
+
const groupId = pkg2.name.slice(0, colonIdx);
|
|
3060
|
+
const artifactId = pkg2.name.slice(colonIdx + 1);
|
|
3061
|
+
return `pkg:maven/${encodeURIComponent(groupId)}/${encodeURIComponent(artifactId)}@${pkg2.version}`;
|
|
3062
|
+
}
|
|
3063
|
+
return `pkg:maven/${encodeURIComponent(pkg2.name)}@${pkg2.version}`;
|
|
3064
|
+
}
|
|
3065
|
+
default:
|
|
3066
|
+
return `pkg:generic/${encodeURIComponent(pkg2.name)}@${pkg2.version}`;
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
function mapViolationToVuln(violation) {
|
|
3070
|
+
const cveRefs = violation.constraintViolations.flatMap(
|
|
3071
|
+
(cv) => cv.reasons.filter((r) => r.reference?.type === "VULNERABILITY").map((r) => r.reference.value)
|
|
3072
|
+
);
|
|
3073
|
+
const id = cveRefs[0] ?? violation.policyId;
|
|
3074
|
+
const severity = threatLevelToSeverity(violation.policyThreatLevel);
|
|
3075
|
+
return {
|
|
3076
|
+
id,
|
|
3077
|
+
summary: violation.policyName,
|
|
3078
|
+
severity,
|
|
3079
|
+
cvss: null,
|
|
3080
|
+
aliases: [...new Set(cveRefs)],
|
|
3081
|
+
remediation: {
|
|
3082
|
+
fixAvailable: false,
|
|
3083
|
+
fixedVersions: [],
|
|
3084
|
+
advice: `Policy violation: ${violation.policyName} (threat level ${violation.policyThreatLevel}). Check Sonatype IQ Server for details.`
|
|
3085
|
+
},
|
|
3086
|
+
references: []
|
|
3087
|
+
};
|
|
3088
|
+
}
|
|
3089
|
+
function syntheticVulnsFromCounts(criticalCount, severeCount, moderateCount, pkgName) {
|
|
3090
|
+
const vulns = [];
|
|
3091
|
+
if (criticalCount > 0) {
|
|
3092
|
+
vulns.push({
|
|
3093
|
+
id: `iq-critical-${pkgName}`,
|
|
3094
|
+
summary: `${criticalCount} critical vulnerability(ies) detected by Sonatype IQ`,
|
|
3095
|
+
severity: "CRITICAL",
|
|
3096
|
+
cvss: null,
|
|
3097
|
+
aliases: [],
|
|
3098
|
+
remediation: { fixAvailable: false, fixedVersions: [], advice: "Check Sonatype IQ Server for details." },
|
|
3099
|
+
references: []
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
if (severeCount > 0) {
|
|
3103
|
+
vulns.push({
|
|
3104
|
+
id: `iq-high-${pkgName}`,
|
|
3105
|
+
summary: `${severeCount} severe vulnerability(ies) detected by Sonatype IQ`,
|
|
3106
|
+
severity: "HIGH",
|
|
3107
|
+
cvss: null,
|
|
3108
|
+
aliases: [],
|
|
3109
|
+
remediation: { fixAvailable: false, fixedVersions: [], advice: "Check Sonatype IQ Server for details." },
|
|
3110
|
+
references: []
|
|
3111
|
+
});
|
|
3112
|
+
}
|
|
3113
|
+
if (moderateCount > 0) {
|
|
3114
|
+
vulns.push({
|
|
3115
|
+
id: `iq-medium-${pkgName}`,
|
|
3116
|
+
summary: `${moderateCount} moderate vulnerability(ies) detected by Sonatype IQ`,
|
|
3117
|
+
severity: "MEDIUM",
|
|
3118
|
+
cvss: null,
|
|
3119
|
+
aliases: [],
|
|
3120
|
+
remediation: { fixAvailable: false, fixedVersions: [], advice: "Check Sonatype IQ Server for details." },
|
|
3121
|
+
references: []
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
3124
|
+
return vulns;
|
|
3125
|
+
}
|
|
3126
|
+
async function resolveApplicationInternalId(baseUrl, applicationId, auth) {
|
|
3127
|
+
const url = `${baseUrl.replace(/\/$/, "")}/api/v2/applications?publicId=${encodeURIComponent(applicationId)}`;
|
|
3128
|
+
const res = await fetchWithTimeout3(url, {
|
|
3129
|
+
method: "GET",
|
|
3130
|
+
headers: { "Authorization": auth, "Accept": "application/json" }
|
|
3131
|
+
});
|
|
3132
|
+
if (!res.ok) return applicationId;
|
|
3133
|
+
const data = await res.json();
|
|
3134
|
+
if (data.applications && data.applications.length > 0) {
|
|
3135
|
+
return data.applications[0].id;
|
|
3136
|
+
}
|
|
3137
|
+
return applicationId;
|
|
3138
|
+
}
|
|
3139
|
+
async function pollResults(baseUrl, resultsUrl, auth) {
|
|
3140
|
+
for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
|
|
3141
|
+
if (attempt > 0) {
|
|
3142
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS * attempt));
|
|
3143
|
+
}
|
|
3144
|
+
const url = resultsUrl.startsWith("http") ? resultsUrl : `${baseUrl.replace(/\/$/, "")}${resultsUrl}`;
|
|
3145
|
+
const res = await fetchWithTimeout3(url, {
|
|
3146
|
+
method: "GET",
|
|
3147
|
+
headers: { "Authorization": auth, "Accept": "application/json" }
|
|
3148
|
+
});
|
|
3149
|
+
if (!res.ok) {
|
|
3150
|
+
throw new Error(`Sonatype IQ Server returned HTTP ${res.status} polling results`);
|
|
3151
|
+
}
|
|
3152
|
+
const data = await res.json();
|
|
3153
|
+
if (!data.isQueued) return data;
|
|
3154
|
+
}
|
|
3155
|
+
throw new Error("Sonatype IQ Server evaluation timed out waiting for results.");
|
|
3156
|
+
}
|
|
3157
|
+
async function evaluateBatch(purls, packages, baseUrl, applicationId, auth) {
|
|
3158
|
+
const url = `${baseUrl.replace(/\/$/, "")}/api/v2/evaluation/applications/${encodeURIComponent(applicationId)}`;
|
|
3159
|
+
const res = await fetchWithTimeout3(url, {
|
|
3160
|
+
method: "POST",
|
|
3161
|
+
headers: { "Authorization": auth, "Content-Type": "application/json", "Accept": "application/json" },
|
|
3162
|
+
body: JSON.stringify({ components: purls.map((p) => ({ packageUrl: p })) })
|
|
3163
|
+
});
|
|
3164
|
+
if (!res.ok) {
|
|
3165
|
+
throw new Error(`Sonatype IQ Server returned HTTP ${res.status}`);
|
|
3166
|
+
}
|
|
3167
|
+
let data = await res.json();
|
|
3168
|
+
if (data.isQueued && data.resultsUrl) {
|
|
3169
|
+
data = await pollResults(baseUrl, data.resultsUrl, auth);
|
|
3170
|
+
}
|
|
3171
|
+
const components = data.components ?? [];
|
|
3172
|
+
const vulnerable = [];
|
|
3173
|
+
const purlToPackage = /* @__PURE__ */ new Map();
|
|
3174
|
+
for (let i = 0; i < purls.length; i++) {
|
|
3175
|
+
purlToPackage.set(purls[i], packages[i]);
|
|
3176
|
+
}
|
|
3177
|
+
for (const result of components) {
|
|
3178
|
+
if (!result?.component?.packageUrl) continue;
|
|
3179
|
+
const pkg2 = purlToPackage.get(result.component.packageUrl);
|
|
3180
|
+
if (!pkg2) continue;
|
|
3181
|
+
const criticalCount = result.criticalVulnerabilityCount ?? 0;
|
|
3182
|
+
const severeCount = result.severeVulnerabilityCount ?? 0;
|
|
3183
|
+
const moderateCount = result.moderateVulnerabilityCount ?? 0;
|
|
3184
|
+
const totalVulns = criticalCount + severeCount + moderateCount;
|
|
3185
|
+
const violations = result.policyData?.policyViolations ?? [];
|
|
3186
|
+
const securityViolations = violations.filter((v) => v.policyThreatCategory === "SECURITY");
|
|
3187
|
+
if (securityViolations.length === 0 && totalVulns === 0) continue;
|
|
3188
|
+
const vulnerabilities = securityViolations.length > 0 ? securityViolations.map((v) => mapViolationToVuln(v)) : syntheticVulnsFromCounts(criticalCount, severeCount, moderateCount, pkg2.name);
|
|
3189
|
+
vulnerable.push({ ...pkg2, vulnerabilities });
|
|
3190
|
+
}
|
|
3191
|
+
return vulnerable;
|
|
3192
|
+
}
|
|
3193
|
+
async function checkPackagesViaIqServer(packages, applicationId, config) {
|
|
3194
|
+
const { baseUrl, userCode, passcode } = config.sonatypeiq;
|
|
3195
|
+
if (!baseUrl) throw new Error("Sonatype IQ Server baseUrl not configured.");
|
|
3196
|
+
if (!userCode || !passcode) throw new Error("Sonatype IQ Server credentials not configured.");
|
|
3197
|
+
const auth = buildBasicAuth(userCode, passcode);
|
|
3198
|
+
const internalId = await resolveApplicationInternalId(baseUrl, applicationId, auth);
|
|
3199
|
+
const allVulnerable = [];
|
|
3200
|
+
for (let i = 0; i < packages.length; i += BATCH_SIZE3) {
|
|
3201
|
+
const chunk = packages.slice(i, i + BATCH_SIZE3);
|
|
3202
|
+
const purls = chunk.map(toPurl2);
|
|
3203
|
+
const results = await evaluateBatch(purls, chunk, baseUrl, internalId, auth);
|
|
3204
|
+
allVulnerable.push(...results);
|
|
3205
|
+
}
|
|
3206
|
+
return allVulnerable;
|
|
3207
|
+
}
|
|
3208
|
+
async function checkSonatypeIqConnectivity(baseUrl, userCode, passcode) {
|
|
3209
|
+
try {
|
|
3210
|
+
const auth = buildBasicAuth(userCode, passcode);
|
|
3211
|
+
const url = `${baseUrl.replace(/\/$/, "")}/api/v2/applications?limit=1`;
|
|
3212
|
+
const controller = new AbortController();
|
|
3213
|
+
const timer = setTimeout(() => controller.abort(), 1e4);
|
|
3214
|
+
let res;
|
|
3215
|
+
try {
|
|
3216
|
+
res = await fetch(url, {
|
|
3217
|
+
method: "GET",
|
|
3218
|
+
headers: { "Authorization": auth, "Accept": "application/json" },
|
|
3219
|
+
signal: controller.signal
|
|
3220
|
+
});
|
|
3221
|
+
} finally {
|
|
3222
|
+
clearTimeout(timer);
|
|
3223
|
+
}
|
|
3224
|
+
if (res.status === 401 || res.status === 403) {
|
|
3225
|
+
return { reachable: true, authenticated: false, error: `Authentication failed (HTTP ${res.status})` };
|
|
3226
|
+
}
|
|
3227
|
+
return { reachable: true, authenticated: res.ok };
|
|
3228
|
+
} catch (err) {
|
|
3229
|
+
return {
|
|
3230
|
+
reachable: false,
|
|
3231
|
+
authenticated: false,
|
|
3232
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3233
|
+
};
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
// src/services/deps/clients/artifactory.ts
|
|
3238
|
+
var TIMEOUT_MS4 = 15e3;
|
|
3239
|
+
async function fetchWithTimeout4(url, token) {
|
|
3240
|
+
const controller = new AbortController();
|
|
3241
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS4);
|
|
3029
3242
|
try {
|
|
3030
3243
|
return await fetch(url, {
|
|
3031
3244
|
headers: { Authorization: `Bearer ${token}` },
|
|
@@ -3061,7 +3274,7 @@ async function checkArtifactoryConnectivity(config) {
|
|
|
3061
3274
|
};
|
|
3062
3275
|
}
|
|
3063
3276
|
try {
|
|
3064
|
-
const res = await
|
|
3277
|
+
const res = await fetchWithTimeout4(`${config.baseUrl}/api/system/ping`, config.token);
|
|
3065
3278
|
if (res.status === 401 || res.status === 403) {
|
|
3066
3279
|
return {
|
|
3067
3280
|
reachable: true,
|
|
@@ -3111,7 +3324,7 @@ function repoForEcosystem(config, eco) {
|
|
|
3111
3324
|
async function getLatestNpm(baseUrl, token, repoName, packageName) {
|
|
3112
3325
|
try {
|
|
3113
3326
|
const encoded = packageName.startsWith("@") ? packageName.replace("/", "%2F") : packageName;
|
|
3114
|
-
const res = await
|
|
3327
|
+
const res = await fetchWithTimeout4(`${baseUrl}/api/npm/${repoName}/${encoded}`, token);
|
|
3115
3328
|
if (!res.ok) return null;
|
|
3116
3329
|
const data = await res.json();
|
|
3117
3330
|
return data["dist-tags"]?.latest ?? null;
|
|
@@ -3122,7 +3335,7 @@ async function getLatestNpm(baseUrl, token, repoName, packageName) {
|
|
|
3122
3335
|
async function getLatestNuget(baseUrl, token, repoName, packageName) {
|
|
3123
3336
|
try {
|
|
3124
3337
|
const lower = packageName.toLowerCase();
|
|
3125
|
-
const res = await
|
|
3338
|
+
const res = await fetchWithTimeout4(
|
|
3126
3339
|
`${baseUrl}/api/nuget/v3/${repoName}/flatcontainer/${lower}/index.json`,
|
|
3127
3340
|
token
|
|
3128
3341
|
);
|
|
@@ -3138,7 +3351,7 @@ async function getLatestMaven(baseUrl, token, repoName, packageName) {
|
|
|
3138
3351
|
const [groupId, artifactId] = packageName.split(":");
|
|
3139
3352
|
if (!groupId || !artifactId) return null;
|
|
3140
3353
|
try {
|
|
3141
|
-
const res = await
|
|
3354
|
+
const res = await fetchWithTimeout4(
|
|
3142
3355
|
`${baseUrl}/api/search/latestVersion?g=${encodeURIComponent(groupId)}&a=${encodeURIComponent(artifactId)}&repos=${repoName}`,
|
|
3143
3356
|
token
|
|
3144
3357
|
);
|
|
@@ -3188,7 +3401,7 @@ async function getOutdatedPackages(packages, config, filterType) {
|
|
|
3188
3401
|
async function getLicenseNpm(baseUrl, token, repoName, packageName) {
|
|
3189
3402
|
try {
|
|
3190
3403
|
const encoded = packageName.startsWith("@") ? packageName.replace("/", "%2F") : packageName;
|
|
3191
|
-
const res = await
|
|
3404
|
+
const res = await fetchWithTimeout4(`${baseUrl}/api/npm/${repoName}/${encoded}`, token);
|
|
3192
3405
|
if (!res.ok) return null;
|
|
3193
3406
|
const data = await res.json();
|
|
3194
3407
|
if (typeof data.license === "string") return data.license;
|
|
@@ -3201,7 +3414,7 @@ async function getLicenseNpm(baseUrl, token, repoName, packageName) {
|
|
|
3201
3414
|
async function getLicenseNuget(baseUrl, token, repoName, packageName, version) {
|
|
3202
3415
|
try {
|
|
3203
3416
|
const lower = packageName.toLowerCase();
|
|
3204
|
-
const res = await
|
|
3417
|
+
const res = await fetchWithTimeout4(
|
|
3205
3418
|
`${baseUrl}/api/nuget/v3/${repoName}/registration/${lower}/${version}.json`,
|
|
3206
3419
|
token
|
|
3207
3420
|
);
|
|
@@ -3258,11 +3471,15 @@ async function checkSonatypeReachable() {
|
|
|
3258
3471
|
return result.reachable;
|
|
3259
3472
|
}
|
|
3260
3473
|
async function buildConnectivityData(config) {
|
|
3261
|
-
const
|
|
3474
|
+
const iqCfg = config.sonatypeiq;
|
|
3475
|
+
const iqConfigured = !!(iqCfg.baseUrl && iqCfg.userCode && iqCfg.passcode);
|
|
3476
|
+
const checks = await Promise.all([
|
|
3262
3477
|
checkOsvConnectivity(),
|
|
3263
3478
|
checkArtifactoryConnectivity(config.artifactory),
|
|
3264
|
-
checkSonatypeConnectivity()
|
|
3479
|
+
checkSonatypeConnectivity(),
|
|
3480
|
+
iqConfigured ? checkSonatypeIqConnectivity(iqCfg.baseUrl, iqCfg.userCode, iqCfg.passcode) : Promise.resolve(null)
|
|
3265
3481
|
]);
|
|
3482
|
+
const [osvResult, artResult, sonatypeResult, iqResult] = checks;
|
|
3266
3483
|
const artCfg = config.artifactory;
|
|
3267
3484
|
let tier = "local";
|
|
3268
3485
|
if (artResult.reachable && artResult.authenticated) tier = "artifactory";
|
|
@@ -3294,13 +3511,21 @@ async function buildConnectivityData(config) {
|
|
|
3294
3511
|
url: "https://ossindex.sonatype.org",
|
|
3295
3512
|
...sonatypeResult.error ? { error: sonatypeResult.error } : {}
|
|
3296
3513
|
},
|
|
3514
|
+
...iqConfigured && iqResult ? {
|
|
3515
|
+
sonatypeiq: {
|
|
3516
|
+
reachable: iqResult.reachable,
|
|
3517
|
+
authenticated: iqResult.authenticated,
|
|
3518
|
+
url: iqCfg.baseUrl,
|
|
3519
|
+
...iqResult.error ? { error: iqResult.error } : {}
|
|
3520
|
+
}
|
|
3521
|
+
} : {},
|
|
3297
3522
|
tier,
|
|
3298
3523
|
capabilities: {
|
|
3299
3524
|
scan: true,
|
|
3300
3525
|
diff: true,
|
|
3301
3526
|
outdated: artResult.reachable && artResult.authenticated,
|
|
3302
3527
|
licenseCheck: artResult.reachable && artResult.authenticated,
|
|
3303
|
-
cveCheck: osvResult.reachable || sonatypeResult.reachable
|
|
3528
|
+
cveCheck: osvResult.reachable || sonatypeResult.reachable || (iqResult?.reachable && iqResult?.authenticated) === true
|
|
3304
3529
|
}
|
|
3305
3530
|
};
|
|
3306
3531
|
}
|
|
@@ -3309,13 +3534,27 @@ async function buildConnectivityData(config) {
|
|
|
3309
3534
|
function toErrorMessage(err) {
|
|
3310
3535
|
return err instanceof Error ? err.message : String(err);
|
|
3311
3536
|
}
|
|
3312
|
-
async function runFrisk(config, opts, source = "osv") {
|
|
3537
|
+
async function runFrisk(config, opts, source = "osv", applicationId) {
|
|
3313
3538
|
const repoRoot = getRepoRoot();
|
|
3314
3539
|
if (!repoRoot) {
|
|
3315
3540
|
throw new PncliError("Not inside a git repository.", 1);
|
|
3316
3541
|
}
|
|
3542
|
+
if (source === "sonatypeiq") {
|
|
3543
|
+
if (!config.sonatypeiq.baseUrl || !config.sonatypeiq.userCode || !config.sonatypeiq.passcode) {
|
|
3544
|
+
throw new PncliError(
|
|
3545
|
+
"deps frisk --source sonatypeiq requires Sonatype IQ Server to be configured. Run: pncli config init",
|
|
3546
|
+
1
|
|
3547
|
+
);
|
|
3548
|
+
}
|
|
3549
|
+
if (!applicationId) {
|
|
3550
|
+
throw new PncliError(
|
|
3551
|
+
"deps frisk --source sonatypeiq requires --application-id <id>. Use 'pncli sonatypeiq applications list' to find application IDs.",
|
|
3552
|
+
1
|
|
3553
|
+
);
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3317
3556
|
const { tier: baseTier, osvReachable } = await detectTier(config);
|
|
3318
|
-
const sonatypeReachable = source
|
|
3557
|
+
const sonatypeReachable = source === "sonatype" || source === "all" ? await checkSonatypeReachable() : false;
|
|
3319
3558
|
const tier = sonatypeReachable ? "full" : baseTier;
|
|
3320
3559
|
if (source === "osv" && !osvReachable) {
|
|
3321
3560
|
throw new PncliError(
|
|
@@ -3356,7 +3595,9 @@ async function runFrisk(config, opts, source = "osv") {
|
|
|
3356
3595
|
return { tier, source, sourcesQueried, sourceErrors, scanned: 0, vulnerable: 0, packages: [] };
|
|
3357
3596
|
}
|
|
3358
3597
|
let vulnerable;
|
|
3359
|
-
if (source === "
|
|
3598
|
+
if (source === "sonatypeiq") {
|
|
3599
|
+
vulnerable = await checkPackagesViaIqServer(scan.packages, applicationId, config);
|
|
3600
|
+
} else if (source === "sonatype") {
|
|
3360
3601
|
vulnerable = await checkPackagesForVulnsViaSonatype(scan.packages);
|
|
3361
3602
|
} else if (source === "all") {
|
|
3362
3603
|
const [osvSettled, sonatypeSettled] = await Promise.allSettled([
|
|
@@ -3491,7 +3732,7 @@ async function runLicenseCheck(config, opts) {
|
|
|
3491
3732
|
// src/services/deps/commands.ts
|
|
3492
3733
|
function registerDepsCommands(program2) {
|
|
3493
3734
|
const deps = program2.command("deps").description("Dependency scanning, CVE detection, and license auditing");
|
|
3494
|
-
deps.command("frisk").description("Scan all dependencies for CVEs and output remediation paths (requires OSV.dev or Sonatype
|
|
3735
|
+
deps.command("frisk").description("Scan all dependencies for CVEs and output remediation paths (requires OSV.dev, Sonatype OSS Index, or Sonatype IQ Server)").option("--ecosystem <ecosystem>", "Filter to one ecosystem: npm, nuget, maven, all", "all").option("--direct-only", "Only scan direct dependencies (default: include transitive)", false).option("--include-dev", "Include dev/test dependencies", false).addOption(new Option("--source <source>", "Vulnerability source").choices(["osv", "sonatype", "sonatypeiq", "all"]).default("osv")).option("--application-id <id>", "Sonatype IQ Server application ID (required when --source sonatypeiq)").action(async (opts, cmd) => {
|
|
3495
3736
|
const startTime = Date.now();
|
|
3496
3737
|
try {
|
|
3497
3738
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -3501,7 +3742,7 @@ function registerDepsCommands(program2) {
|
|
|
3501
3742
|
includeTransitive: !opts.directOnly,
|
|
3502
3743
|
includeDev: opts.includeDev
|
|
3503
3744
|
};
|
|
3504
|
-
const data = await runFrisk(config, scanOpts, opts.source);
|
|
3745
|
+
const data = await runFrisk(config, scanOpts, opts.source, opts.applicationId);
|
|
3505
3746
|
success(data, "deps", "frisk", startTime);
|
|
3506
3747
|
} catch (err) {
|
|
3507
3748
|
fail(err, "deps", "frisk", startTime);
|
|
@@ -3894,6 +4135,16 @@ function registerConfigCommands(program2) {
|
|
|
3894
4135
|
} else {
|
|
3895
4136
|
results.contrast = { ok: null, message: "not configured" };
|
|
3896
4137
|
}
|
|
4138
|
+
if (cfg.sonatypeiq.baseUrl && cfg.sonatypeiq.userCode && cfg.sonatypeiq.passcode) {
|
|
4139
|
+
try {
|
|
4140
|
+
await http.sonatypeiq("/api/v2/applications", { params: { limit: 1 } });
|
|
4141
|
+
results.sonatypeiq = { ok: true, message: "connected" };
|
|
4142
|
+
} catch (err) {
|
|
4143
|
+
results.sonatypeiq = { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
4144
|
+
}
|
|
4145
|
+
} else {
|
|
4146
|
+
results.sonatypeiq = { ok: null, message: "not configured" };
|
|
4147
|
+
}
|
|
3897
4148
|
success(results, "config", "test", start);
|
|
3898
4149
|
} catch (err) {
|
|
3899
4150
|
fail(err, "config", "test", start);
|
|
@@ -4065,11 +4316,23 @@ function registerConfigCommands(program2) {
|
|
|
4065
4316
|
results.contrast = categorize2(err);
|
|
4066
4317
|
}
|
|
4067
4318
|
}
|
|
4319
|
+
if (!cfg.sonatypeiq.userCode || !cfg.sonatypeiq.passcode) {
|
|
4320
|
+
results.sonatypeiq = { status: "blank", message: "not configured" };
|
|
4321
|
+
} else if (!cfg.sonatypeiq.baseUrl) {
|
|
4322
|
+
results.sonatypeiq = { status: "error", message: "baseUrl not configured" };
|
|
4323
|
+
} else {
|
|
4324
|
+
try {
|
|
4325
|
+
await http.sonatypeiq("/api/v2/applications", { params: { limit: 1 }, timeoutMs: 1e4 });
|
|
4326
|
+
results.sonatypeiq = { status: "valid", message: "ok" };
|
|
4327
|
+
} catch (err) {
|
|
4328
|
+
results.sonatypeiq = categorize2(err);
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4068
4331
|
const statuses = Object.values(results).map((r) => r.status);
|
|
4069
4332
|
if (statuses.includes("invalid")) process.exitCode = ExitCode.AUTH_ERROR;
|
|
4070
4333
|
else if (statuses.includes("error")) process.exitCode = ExitCode.NETWORK_ERROR;
|
|
4071
4334
|
if (cmdOpts.output === "table") {
|
|
4072
|
-
const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast"];
|
|
4335
|
+
const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq"];
|
|
4073
4336
|
const labelWidth = 14;
|
|
4074
4337
|
const statusWidth = 9;
|
|
4075
4338
|
for (const svc of services) {
|
|
@@ -4083,7 +4346,7 @@ function registerConfigCommands(program2) {
|
|
|
4083
4346
|
process.stdout.write("\n");
|
|
4084
4347
|
} else {
|
|
4085
4348
|
if (opts.pretty) {
|
|
4086
|
-
const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast"];
|
|
4349
|
+
const services = ["jira", "bitbucket", "confluence", "sonar", "sde", "ado", "jenkins", "udeploy", "artifactory", "checkmarx", "servicenow", "contrast", "sonatypeiq"];
|
|
4087
4350
|
const labelWidth = 14;
|
|
4088
4351
|
const statusWidth = 9;
|
|
4089
4352
|
for (const svc of services) {
|
|
@@ -4471,6 +4734,47 @@ async function initGlobalConfig(start) {
|
|
|
4471
4734
|
}
|
|
4472
4735
|
}
|
|
4473
4736
|
}
|
|
4737
|
+
process.stderr.write("\n\u2500\u2500 Sonatype IQ Server \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
4738
|
+
const useSonatypeIq = await confirm({
|
|
4739
|
+
message: "Configure Sonatype IQ Server for dependency vulnerability scanning?",
|
|
4740
|
+
default: false
|
|
4741
|
+
});
|
|
4742
|
+
let sonatypeIqBaseUrl = "";
|
|
4743
|
+
let sonatypeIqUserCode = "";
|
|
4744
|
+
let sonatypeIqPasscode = "";
|
|
4745
|
+
if (useSonatypeIq) {
|
|
4746
|
+
process.stderr.write(" User Token credentials are found in your IQ Server profile under User Token.\n");
|
|
4747
|
+
sonatypeIqBaseUrl = await input({
|
|
4748
|
+
message: "Sonatype IQ Server base URL (e.g. https://iq.your-company.com):",
|
|
4749
|
+
default: ""
|
|
4750
|
+
});
|
|
4751
|
+
sonatypeIqUserCode = await input({
|
|
4752
|
+
message: "User Code (from User Token):",
|
|
4753
|
+
default: ""
|
|
4754
|
+
});
|
|
4755
|
+
sonatypeIqPasscode = await password({
|
|
4756
|
+
message: "Passcode (from User Token):"
|
|
4757
|
+
});
|
|
4758
|
+
if (sonatypeIqBaseUrl && sonatypeIqUserCode && sonatypeIqPasscode) {
|
|
4759
|
+
process.stderr.write("\n Verifying connection...\n");
|
|
4760
|
+
try {
|
|
4761
|
+
const tempConfig = {
|
|
4762
|
+
...loadConfig(),
|
|
4763
|
+
sonatypeiq: {
|
|
4764
|
+
baseUrl: sonatypeIqBaseUrl,
|
|
4765
|
+
userCode: sonatypeIqUserCode,
|
|
4766
|
+
passcode: sonatypeIqPasscode
|
|
4767
|
+
}
|
|
4768
|
+
};
|
|
4769
|
+
const tempHttp = createHttpClient(tempConfig);
|
|
4770
|
+
await tempHttp.sonatypeiq("/api/v2/applications", { params: { limit: 1 } });
|
|
4771
|
+
process.stderr.write(" Connected.\n");
|
|
4772
|
+
} catch (err) {
|
|
4773
|
+
warn(`Could not connect to Sonatype IQ Server: ${err instanceof Error ? err.message : String(err)}`);
|
|
4774
|
+
warn("Config will be saved anyway. Check your URL and credentials and re-run pncli config init or pncli config test.");
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4474
4778
|
process.stderr.write("\n\u2500\u2500 Contrast IAST \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
4475
4779
|
const useContrast = await confirm({
|
|
4476
4780
|
message: "Configure Contrast Security IAST for vulnerability findings?",
|
|
@@ -4634,6 +4938,13 @@ async function initGlobalConfig(start) {
|
|
|
4634
4938
|
serviceKey: contrastServiceKey || void 0
|
|
4635
4939
|
}
|
|
4636
4940
|
} : {},
|
|
4941
|
+
...useSonatypeIq && sonatypeIqBaseUrl ? {
|
|
4942
|
+
sonatypeiq: {
|
|
4943
|
+
baseUrl: sonatypeIqBaseUrl,
|
|
4944
|
+
userCode: sonatypeIqUserCode || void 0,
|
|
4945
|
+
passcode: sonatypeIqPasscode || void 0
|
|
4946
|
+
}
|
|
4947
|
+
} : {},
|
|
4637
4948
|
defaults: {
|
|
4638
4949
|
jira: {
|
|
4639
4950
|
project: jiraProject || void 0
|
|
@@ -5538,11 +5849,11 @@ function registerAdoRepoCommands(ado) {
|
|
|
5538
5849
|
const start = Date.now();
|
|
5539
5850
|
try {
|
|
5540
5851
|
const globalOpts = ado.optsWithGlobals();
|
|
5541
|
-
const { loadConfig: lc } = await import("./config-
|
|
5852
|
+
const { loadConfig: lc } = await import("./config-NCI2BTQG.js");
|
|
5542
5853
|
const config = lc({ configPath: globalOpts.config });
|
|
5543
5854
|
const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
|
|
5544
5855
|
const { AdoCoreClient: AdoCoreClient2 } = await import("./core-SWYVXOQ4.js");
|
|
5545
|
-
const { createHttpClient: createHttpClient2 } = await import("./http-
|
|
5856
|
+
const { createHttpClient: createHttpClient2 } = await import("./http-L2P23H2D.js");
|
|
5546
5857
|
const http = createHttpClient2(config, Boolean(globalOpts.dryRun));
|
|
5547
5858
|
const coreClient = new AdoCoreClient2(http);
|
|
5548
5859
|
const me = await coreClient.getConnectionData(collection);
|
|
@@ -6154,7 +6465,7 @@ var UdeployClient = class {
|
|
|
6154
6465
|
};
|
|
6155
6466
|
|
|
6156
6467
|
// src/services/udeploy/commands.ts
|
|
6157
|
-
var
|
|
6468
|
+
var POLL_INTERVAL_MS2 = 5e3;
|
|
6158
6469
|
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
6159
6470
|
function getClient8(program2) {
|
|
6160
6471
|
const opts = program2.optsWithGlobals();
|
|
@@ -6265,7 +6576,7 @@ function registerUdeployCommands(program2) {
|
|
|
6265
6576
|
if (elapsed >= timeoutMs) {
|
|
6266
6577
|
throw new PncliError(`Timed out after ${timeoutMs}ms waiting for request ${requestId}`, 1);
|
|
6267
6578
|
}
|
|
6268
|
-
await sleep(
|
|
6579
|
+
await sleep(POLL_INTERVAL_MS2);
|
|
6269
6580
|
const status = await client.getRequestStatus(requestId);
|
|
6270
6581
|
if (status.status === "CLOSED") {
|
|
6271
6582
|
const outcome = { requestId, status: status.status, result: status.result, elapsed_ms: Date.now() - start };
|
|
@@ -6584,19 +6895,87 @@ function registerContrastCommands(program2) {
|
|
|
6584
6895
|
});
|
|
6585
6896
|
}
|
|
6586
6897
|
|
|
6898
|
+
// src/services/sonatypeiq/commands.ts
|
|
6899
|
+
function getHttpClient(program2) {
|
|
6900
|
+
const opts = program2.optsWithGlobals();
|
|
6901
|
+
const config = loadConfig({ configPath: opts.config });
|
|
6902
|
+
if (!config.sonatypeiq.baseUrl) throw new PncliError("Sonatype IQ Server baseUrl not configured. Run: pncli config init");
|
|
6903
|
+
if (!config.sonatypeiq.userCode || !config.sonatypeiq.passcode) throw new PncliError("Sonatype IQ Server credentials not configured. Run: pncli config init");
|
|
6904
|
+
const http = createHttpClient(config, Boolean(opts.dryRun));
|
|
6905
|
+
return http;
|
|
6906
|
+
}
|
|
6907
|
+
function registerSonatypeIqCommands(program2) {
|
|
6908
|
+
const sonatypeiq = program2.command("sonatypeiq").description("Sonatype IQ Server operations");
|
|
6909
|
+
const applications = sonatypeiq.command("applications").description("Application operations");
|
|
6910
|
+
applications.command("list").description("List all applications in Sonatype IQ Server").option("--organization-id <id>", "Filter by organization ID").action(async (opts) => {
|
|
6911
|
+
const startTime = Date.now();
|
|
6912
|
+
try {
|
|
6913
|
+
const http = getHttpClient(program2);
|
|
6914
|
+
const params = {};
|
|
6915
|
+
if (opts.organizationId) params["organizationId"] = opts.organizationId;
|
|
6916
|
+
const data = await http.sonatypeiq("/api/v2/applications", { params });
|
|
6917
|
+
success(data, "sonatypeiq", "applications list", startTime);
|
|
6918
|
+
} catch (err) {
|
|
6919
|
+
fail(err, "sonatypeiq", "applications list", startTime);
|
|
6920
|
+
}
|
|
6921
|
+
});
|
|
6922
|
+
applications.command("get").description("Get an application by its public ID").requiredOption("--public-id <id>", "Application public ID").action(async (opts) => {
|
|
6923
|
+
const startTime = Date.now();
|
|
6924
|
+
try {
|
|
6925
|
+
const http = getHttpClient(program2);
|
|
6926
|
+
const data = await http.sonatypeiq("/api/v2/applications", {
|
|
6927
|
+
params: { publicId: opts.publicId }
|
|
6928
|
+
});
|
|
6929
|
+
success(data, "sonatypeiq", "applications get", startTime);
|
|
6930
|
+
} catch (err) {
|
|
6931
|
+
fail(err, "sonatypeiq", "applications get", startTime);
|
|
6932
|
+
}
|
|
6933
|
+
});
|
|
6934
|
+
const organizations = sonatypeiq.command("organizations").description("Organization operations");
|
|
6935
|
+
organizations.command("list").description("List all organizations in Sonatype IQ Server").action(async () => {
|
|
6936
|
+
const startTime = Date.now();
|
|
6937
|
+
try {
|
|
6938
|
+
const http = getHttpClient(program2);
|
|
6939
|
+
const data = await http.sonatypeiq("/api/v2/organizations");
|
|
6940
|
+
success(data, "sonatypeiq", "organizations list", startTime);
|
|
6941
|
+
} catch (err) {
|
|
6942
|
+
fail(err, "sonatypeiq", "organizations list", startTime);
|
|
6943
|
+
}
|
|
6944
|
+
});
|
|
6945
|
+
const policies = sonatypeiq.command("policies").description("Policy operations");
|
|
6946
|
+
policies.command("list").description("List all policies").option("--organization-id <id>", "Filter by organization ID").action(async (opts) => {
|
|
6947
|
+
const startTime = Date.now();
|
|
6948
|
+
try {
|
|
6949
|
+
const http = getHttpClient(program2);
|
|
6950
|
+
const path6 = opts.organizationId ? `/api/v2/policies/organization/${encodeURIComponent(opts.organizationId)}` : "/api/v2/policies";
|
|
6951
|
+
const data = await http.sonatypeiq(path6);
|
|
6952
|
+
success(data, "sonatypeiq", "policies list", startTime);
|
|
6953
|
+
} catch (err) {
|
|
6954
|
+
fail(err, "sonatypeiq", "policies list", startTime);
|
|
6955
|
+
}
|
|
6956
|
+
});
|
|
6957
|
+
}
|
|
6958
|
+
|
|
6587
6959
|
// src/services/skills/commands.ts
|
|
6588
6960
|
import fs3 from "fs";
|
|
6589
6961
|
import path5 from "path";
|
|
6590
6962
|
import os from "os";
|
|
6591
|
-
|
|
6592
|
-
var RAW_BASE = "https://raw.githubusercontent.com/kolatts/pncli/main/skills";
|
|
6963
|
+
import { fileURLToPath } from "url";
|
|
6593
6964
|
var AGENT_PATHS = {
|
|
6594
6965
|
"github-copilot": { project: ".agents/skills", user: path5.join(os.homedir(), ".copilot/skills") },
|
|
6595
6966
|
"claude-code": { project: ".claude/skills", user: path5.join(os.homedir(), ".claude/skills") }
|
|
6596
6967
|
};
|
|
6968
|
+
function getBundledSkillsDir() {
|
|
6969
|
+
try {
|
|
6970
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
6971
|
+
return path5.resolve(path5.dirname(thisFile), "..", "skills");
|
|
6972
|
+
} catch {
|
|
6973
|
+
return "";
|
|
6974
|
+
}
|
|
6975
|
+
}
|
|
6597
6976
|
function registerSkillsCommands(program2) {
|
|
6598
6977
|
const skills = program2.command("skills").description("Manage pncli Claude Code skills");
|
|
6599
|
-
skills.command("install").description("
|
|
6978
|
+
skills.command("install").description("Install pncli skills into the current repo").option("--agent <agent>", "Target agent host: github-copilot | claude-code", "github-copilot").option("--scope <scope>", "Installation scope: project | user", "project").option("--target <dir>", "Override install directory (ignores --agent and --scope)").action((opts) => {
|
|
6600
6979
|
const start = Date.now();
|
|
6601
6980
|
try {
|
|
6602
6981
|
let targetDir;
|
|
@@ -6610,19 +6989,22 @@ function registerSkillsCommands(program2) {
|
|
|
6610
6989
|
const scopePath = opts.scope === "user" ? agentConfig.user : agentConfig.project;
|
|
6611
6990
|
targetDir = path5.resolve(scopePath);
|
|
6612
6991
|
}
|
|
6613
|
-
|
|
6614
|
-
const
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6992
|
+
const resolvedTarget = path5.resolve(targetDir);
|
|
6993
|
+
const bundledDir = getBundledSkillsDir();
|
|
6994
|
+
let skillDirs = [];
|
|
6995
|
+
if (bundledDir !== "") {
|
|
6996
|
+
try {
|
|
6997
|
+
skillDirs = fs3.readdirSync(bundledDir).filter(
|
|
6998
|
+
(name) => fs3.existsSync(path5.join(bundledDir, name, "SKILL.md"))
|
|
6999
|
+
);
|
|
7000
|
+
} catch {
|
|
7001
|
+
}
|
|
6619
7002
|
}
|
|
6620
|
-
const entries = await listRes.json();
|
|
6621
|
-
const skillDirs = entries.filter((e) => e.type === "dir").map((e) => e.name);
|
|
6622
7003
|
if (skillDirs.length === 0) {
|
|
6623
|
-
throw new Error("No skills found
|
|
7004
|
+
throw new Error("No bundled skills found. Reinstall pncli to get the latest version: npm install -g @kolatts/pncli");
|
|
6624
7005
|
}
|
|
6625
|
-
const
|
|
7006
|
+
const installed = [];
|
|
7007
|
+
const failed = [];
|
|
6626
7008
|
for (const skillName of skillDirs) {
|
|
6627
7009
|
const existingDir = path5.resolve(targetDir, skillName);
|
|
6628
7010
|
if (!existingDir.startsWith(resolvedTarget + path5.sep)) continue;
|
|
@@ -6630,25 +7012,15 @@ function registerSkillsCommands(program2) {
|
|
|
6630
7012
|
fs3.rmSync(existingDir, { recursive: true, force: true });
|
|
6631
7013
|
}
|
|
6632
7014
|
}
|
|
6633
|
-
warn(`
|
|
6634
|
-
const installed = [];
|
|
6635
|
-
const failed = [];
|
|
7015
|
+
warn(`Installing ${skillDirs.length} bundled skill(s) to ${targetDir}...`);
|
|
6636
7016
|
for (const skillName of skillDirs) {
|
|
6637
7017
|
const skillDir = path5.resolve(targetDir, skillName);
|
|
6638
7018
|
if (!skillDir.startsWith(resolvedTarget + path5.sep)) {
|
|
6639
7019
|
failed.push(skillName);
|
|
6640
7020
|
continue;
|
|
6641
7021
|
}
|
|
6642
|
-
const skillUrl = `${RAW_BASE}/${skillName}/SKILL.md`;
|
|
6643
7022
|
try {
|
|
6644
|
-
const
|
|
6645
|
-
headers: { "User-Agent": "pncli" }
|
|
6646
|
-
});
|
|
6647
|
-
if (!res.ok) {
|
|
6648
|
-
failed.push(skillName);
|
|
6649
|
-
continue;
|
|
6650
|
-
}
|
|
6651
|
-
const content = await res.text();
|
|
7023
|
+
const content = fs3.readFileSync(path5.join(bundledDir, skillName, "SKILL.md"), "utf8");
|
|
6652
7024
|
fs3.mkdirSync(skillDir, { recursive: true });
|
|
6653
7025
|
fs3.writeFileSync(path5.join(skillDir, "SKILL.md"), content, "utf8");
|
|
6654
7026
|
installed.push(skillName);
|
|
@@ -6658,7 +7030,7 @@ function registerSkillsCommands(program2) {
|
|
|
6658
7030
|
}
|
|
6659
7031
|
warn(`Installed ${installed.length} skill(s) to ${targetDir}`);
|
|
6660
7032
|
if (failed.length > 0) {
|
|
6661
|
-
warn(`Failed to
|
|
7033
|
+
warn(`Failed to install: ${failed.join(", ")}`);
|
|
6662
7034
|
}
|
|
6663
7035
|
success({
|
|
6664
7036
|
installed,
|
|
@@ -6666,7 +7038,8 @@ function registerSkillsCommands(program2) {
|
|
|
6666
7038
|
target: targetDir,
|
|
6667
7039
|
total: installed.length,
|
|
6668
7040
|
agent: opts.target ? "custom" : opts.agent,
|
|
6669
|
-
scope: opts.target ? "custom" : opts.scope
|
|
7041
|
+
scope: opts.target ? "custom" : opts.scope,
|
|
7042
|
+
source: "bundled"
|
|
6670
7043
|
}, "skills", "install", start);
|
|
6671
7044
|
} catch (err) {
|
|
6672
7045
|
fail(err, "skills", "install", start);
|
|
@@ -6772,6 +7145,7 @@ registerUdeployCommands(program);
|
|
|
6772
7145
|
registerCheckmarxCommands(program);
|
|
6773
7146
|
registerServiceNowCommands(program);
|
|
6774
7147
|
registerContrastCommands(program);
|
|
7148
|
+
registerSonatypeIqCommands(program);
|
|
6775
7149
|
registerSkillsCommands(program);
|
|
6776
7150
|
program.addHelpText("after", `
|
|
6777
7151
|
Services:
|
|
@@ -6789,6 +7163,7 @@ Services:
|
|
|
6789
7163
|
checkmarx Checkmarx CxSAST (projects, scans, scan statistics)
|
|
6790
7164
|
servicenow ServiceNow (change requests)
|
|
6791
7165
|
contrast Contrast IAST (applications, vulnerability findings)
|
|
7166
|
+
sonatypeiq Sonatype IQ Server (applications, organizations, policies)
|
|
6792
7167
|
config Manage pncli configuration
|
|
6793
7168
|
skills Download and manage Claude Code skills
|
|
6794
7169
|
`);
|