@kolatts/pncli 1.9.0 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{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 +458 -78
- 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) {
|
|
@@ -4103,6 +4366,11 @@ function registerConfigCommands(program2) {
|
|
|
4103
4366
|
}
|
|
4104
4367
|
});
|
|
4105
4368
|
}
|
|
4369
|
+
function normalizeBaseUrl(raw) {
|
|
4370
|
+
const trimmed = raw.trim();
|
|
4371
|
+
if (!trimmed) return trimmed;
|
|
4372
|
+
return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
4373
|
+
}
|
|
4106
4374
|
async function initGlobalConfig(start) {
|
|
4107
4375
|
process.stderr.write("pncli config init \u2014 Global configuration\n\n");
|
|
4108
4376
|
process.stderr.write("\u2500\u2500 Identity \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\u2500\u2500\u2500\u2500\u2500\n");
|
|
@@ -4116,7 +4384,7 @@ async function initGlobalConfig(start) {
|
|
|
4116
4384
|
});
|
|
4117
4385
|
process.stderr.write("\n\u2500\u2500 Jira \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
4118
4386
|
const jiraBaseUrl = await input({
|
|
4119
|
-
message: "Jira base URL (e.g.
|
|
4387
|
+
message: "Jira base URL (e.g. jira.your-company.com):",
|
|
4120
4388
|
default: ""
|
|
4121
4389
|
});
|
|
4122
4390
|
const jiraApiToken = await password({
|
|
@@ -4124,7 +4392,7 @@ async function initGlobalConfig(start) {
|
|
|
4124
4392
|
});
|
|
4125
4393
|
process.stderr.write("\n\u2500\u2500 Bitbucket \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\u2500\u2500\u2500\u2500\n");
|
|
4126
4394
|
const bitbucketBaseUrl = await input({
|
|
4127
|
-
message: "Bitbucket Server base URL (e.g.
|
|
4395
|
+
message: "Bitbucket Server base URL (e.g. bitbucket.your-company.com):",
|
|
4128
4396
|
default: ""
|
|
4129
4397
|
});
|
|
4130
4398
|
const bitbucketPat = await password({
|
|
@@ -4132,7 +4400,7 @@ async function initGlobalConfig(start) {
|
|
|
4132
4400
|
});
|
|
4133
4401
|
process.stderr.write("\n\u2500\u2500 Confluence \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\u2500\u2500\u2500\n");
|
|
4134
4402
|
const confluenceBaseUrl = await input({
|
|
4135
|
-
message: "Confluence base URL (e.g.
|
|
4403
|
+
message: "Confluence base URL (e.g. confluence.your-company.com):",
|
|
4136
4404
|
default: ""
|
|
4137
4405
|
});
|
|
4138
4406
|
const confluenceApiToken = await password({
|
|
@@ -4150,7 +4418,7 @@ async function initGlobalConfig(start) {
|
|
|
4150
4418
|
let mavenRepo = "";
|
|
4151
4419
|
if (useArtifactory) {
|
|
4152
4420
|
artifactoryBaseUrl = await input({
|
|
4153
|
-
message: "Artifactory base URL (e.g.
|
|
4421
|
+
message: "Artifactory base URL (e.g. artifactory.company.com):",
|
|
4154
4422
|
default: ""
|
|
4155
4423
|
});
|
|
4156
4424
|
artifactoryToken = await password({
|
|
@@ -4179,7 +4447,7 @@ async function initGlobalConfig(start) {
|
|
|
4179
4447
|
let sonarToken = "";
|
|
4180
4448
|
if (useSonar) {
|
|
4181
4449
|
sonarBaseUrl = await input({
|
|
4182
|
-
message: "SonarQube Server base URL (e.g.
|
|
4450
|
+
message: "SonarQube Server base URL (e.g. sonar.your-company.com):",
|
|
4183
4451
|
default: ""
|
|
4184
4452
|
});
|
|
4185
4453
|
sonarToken = await password({
|
|
@@ -4232,7 +4500,7 @@ async function initGlobalConfig(start) {
|
|
|
4232
4500
|
let adoDiscoveredTypes = [];
|
|
4233
4501
|
if (useAdo) {
|
|
4234
4502
|
adoBaseUrl = await input({
|
|
4235
|
-
message: "Azure DevOps Server base URL (e.g.
|
|
4503
|
+
message: "Azure DevOps Server base URL (e.g. tfs.company.com or devops.company.com/tfs):",
|
|
4236
4504
|
default: ""
|
|
4237
4505
|
});
|
|
4238
4506
|
adoPat = await password({
|
|
@@ -4252,7 +4520,7 @@ async function initGlobalConfig(start) {
|
|
|
4252
4520
|
const tempConfig = {
|
|
4253
4521
|
...loadConfig(),
|
|
4254
4522
|
ado: {
|
|
4255
|
-
baseUrl: adoBaseUrl,
|
|
4523
|
+
baseUrl: normalizeBaseUrl(adoBaseUrl),
|
|
4256
4524
|
pat: adoPat || void 0,
|
|
4257
4525
|
fieldAliases: {},
|
|
4258
4526
|
discoveredFields: [],
|
|
@@ -4304,7 +4572,7 @@ async function initGlobalConfig(start) {
|
|
|
4304
4572
|
let jenkinsApiToken = "";
|
|
4305
4573
|
if (useJenkins) {
|
|
4306
4574
|
jenkinsBaseUrl = await input({
|
|
4307
|
-
message: "Jenkins base URL (e.g.
|
|
4575
|
+
message: "Jenkins base URL (e.g. jenkins.your-company.com):",
|
|
4308
4576
|
default: ""
|
|
4309
4577
|
});
|
|
4310
4578
|
jenkinsUsername = await input({
|
|
@@ -4328,7 +4596,7 @@ async function initGlobalConfig(start) {
|
|
|
4328
4596
|
let udeployDefaultEnv = "";
|
|
4329
4597
|
if (useUdeploy) {
|
|
4330
4598
|
udeployBaseUrl = await input({
|
|
4331
|
-
message: "UDeploy base URL (e.g.
|
|
4599
|
+
message: "UDeploy base URL (e.g. ucd.company.com:8443):",
|
|
4332
4600
|
default: ""
|
|
4333
4601
|
});
|
|
4334
4602
|
udeployUsername = await input({
|
|
@@ -4360,7 +4628,7 @@ async function initGlobalConfig(start) {
|
|
|
4360
4628
|
const tempConfig = {
|
|
4361
4629
|
...loadConfig(),
|
|
4362
4630
|
udeploy: {
|
|
4363
|
-
baseUrl: udeployBaseUrl,
|
|
4631
|
+
baseUrl: normalizeBaseUrl(udeployBaseUrl),
|
|
4364
4632
|
pat: udeployPat || void 0,
|
|
4365
4633
|
username: udeployUsername || void 0,
|
|
4366
4634
|
password: udeployPassword || void 0
|
|
@@ -4385,7 +4653,7 @@ async function initGlobalConfig(start) {
|
|
|
4385
4653
|
let checkmarxPassword = "";
|
|
4386
4654
|
if (useCheckmarx) {
|
|
4387
4655
|
checkmarxBaseUrl = await input({
|
|
4388
|
-
message: "Checkmarx base URL (e.g.
|
|
4656
|
+
message: "Checkmarx base URL (e.g. cx.company.com):",
|
|
4389
4657
|
default: ""
|
|
4390
4658
|
});
|
|
4391
4659
|
checkmarxUsername = await input({
|
|
@@ -4402,7 +4670,7 @@ async function initGlobalConfig(start) {
|
|
|
4402
4670
|
const tempConfig = {
|
|
4403
4671
|
...loadConfig(),
|
|
4404
4672
|
checkmarx: {
|
|
4405
|
-
baseUrl: checkmarxBaseUrl,
|
|
4673
|
+
baseUrl: normalizeBaseUrl(checkmarxBaseUrl),
|
|
4406
4674
|
username: checkmarxUsername,
|
|
4407
4675
|
password: checkmarxPassword
|
|
4408
4676
|
}
|
|
@@ -4427,7 +4695,7 @@ async function initGlobalConfig(start) {
|
|
|
4427
4695
|
let servicenowApiToken = "";
|
|
4428
4696
|
if (useServiceNow) {
|
|
4429
4697
|
servicenowBaseUrl = await input({
|
|
4430
|
-
message: "ServiceNow instance URL (e.g.
|
|
4698
|
+
message: "ServiceNow instance URL (e.g. mycompany.service-now.com):",
|
|
4431
4699
|
default: ""
|
|
4432
4700
|
});
|
|
4433
4701
|
servicenowUsername = await input({
|
|
@@ -4456,7 +4724,7 @@ async function initGlobalConfig(start) {
|
|
|
4456
4724
|
const tempConfig = {
|
|
4457
4725
|
...loadConfig(),
|
|
4458
4726
|
servicenow: {
|
|
4459
|
-
baseUrl: servicenowBaseUrl,
|
|
4727
|
+
baseUrl: normalizeBaseUrl(servicenowBaseUrl),
|
|
4460
4728
|
username: servicenowUsername,
|
|
4461
4729
|
password: servicenowPassword || void 0,
|
|
4462
4730
|
apiToken: servicenowApiToken || void 0
|
|
@@ -4471,6 +4739,47 @@ async function initGlobalConfig(start) {
|
|
|
4471
4739
|
}
|
|
4472
4740
|
}
|
|
4473
4741
|
}
|
|
4742
|
+
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");
|
|
4743
|
+
const useSonatypeIq = await confirm({
|
|
4744
|
+
message: "Configure Sonatype IQ Server for dependency vulnerability scanning?",
|
|
4745
|
+
default: false
|
|
4746
|
+
});
|
|
4747
|
+
let sonatypeIqBaseUrl = "";
|
|
4748
|
+
let sonatypeIqUserCode = "";
|
|
4749
|
+
let sonatypeIqPasscode = "";
|
|
4750
|
+
if (useSonatypeIq) {
|
|
4751
|
+
process.stderr.write(" User Token credentials are found in your IQ Server profile under User Token.\n");
|
|
4752
|
+
sonatypeIqBaseUrl = await input({
|
|
4753
|
+
message: "Sonatype IQ Server base URL (e.g. iq.your-company.com):",
|
|
4754
|
+
default: ""
|
|
4755
|
+
});
|
|
4756
|
+
sonatypeIqUserCode = await input({
|
|
4757
|
+
message: "User Code (from User Token):",
|
|
4758
|
+
default: ""
|
|
4759
|
+
});
|
|
4760
|
+
sonatypeIqPasscode = await password({
|
|
4761
|
+
message: "Passcode (from User Token):"
|
|
4762
|
+
});
|
|
4763
|
+
if (sonatypeIqBaseUrl && sonatypeIqUserCode && sonatypeIqPasscode) {
|
|
4764
|
+
process.stderr.write("\n Verifying connection...\n");
|
|
4765
|
+
try {
|
|
4766
|
+
const tempConfig = {
|
|
4767
|
+
...loadConfig(),
|
|
4768
|
+
sonatypeiq: {
|
|
4769
|
+
baseUrl: normalizeBaseUrl(sonatypeIqBaseUrl),
|
|
4770
|
+
userCode: sonatypeIqUserCode,
|
|
4771
|
+
passcode: sonatypeIqPasscode
|
|
4772
|
+
}
|
|
4773
|
+
};
|
|
4774
|
+
const tempHttp = createHttpClient(tempConfig);
|
|
4775
|
+
await tempHttp.sonatypeiq("/api/v2/applications", { params: { limit: 1 } });
|
|
4776
|
+
process.stderr.write(" Connected.\n");
|
|
4777
|
+
} catch (err) {
|
|
4778
|
+
warn(`Could not connect to Sonatype IQ Server: ${err instanceof Error ? err.message : String(err)}`);
|
|
4779
|
+
warn("Config will be saved anyway. Check your URL and credentials and re-run pncli config init or pncli config test.");
|
|
4780
|
+
}
|
|
4781
|
+
}
|
|
4782
|
+
}
|
|
4474
4783
|
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
4784
|
const useContrast = await confirm({
|
|
4476
4785
|
message: "Configure Contrast Security IAST for vulnerability findings?",
|
|
@@ -4508,7 +4817,7 @@ async function initGlobalConfig(start) {
|
|
|
4508
4817
|
const tempConfig = {
|
|
4509
4818
|
...loadConfig(),
|
|
4510
4819
|
contrast: {
|
|
4511
|
-
baseUrl: contrastBaseUrl || void 0,
|
|
4820
|
+
baseUrl: normalizeBaseUrl(contrastBaseUrl) || void 0,
|
|
4512
4821
|
orgUuid: contrastOrgUuid,
|
|
4513
4822
|
username: contrastUsername,
|
|
4514
4823
|
apiKey: contrastApiKey,
|
|
@@ -4553,22 +4862,22 @@ async function initGlobalConfig(start) {
|
|
|
4553
4862
|
userId: userId || void 0
|
|
4554
4863
|
},
|
|
4555
4864
|
jira: {
|
|
4556
|
-
baseUrl: jiraBaseUrl || void 0,
|
|
4865
|
+
baseUrl: normalizeBaseUrl(jiraBaseUrl) || void 0,
|
|
4557
4866
|
apiToken: jiraApiToken || void 0
|
|
4558
4867
|
},
|
|
4559
4868
|
bitbucket: {
|
|
4560
|
-
baseUrl: bitbucketBaseUrl || void 0,
|
|
4869
|
+
baseUrl: normalizeBaseUrl(bitbucketBaseUrl) || void 0,
|
|
4561
4870
|
pat: bitbucketPat || void 0
|
|
4562
4871
|
},
|
|
4563
4872
|
...confluenceBaseUrl || confluenceApiToken ? {
|
|
4564
4873
|
confluence: {
|
|
4565
|
-
baseUrl: confluenceBaseUrl || void 0,
|
|
4874
|
+
baseUrl: normalizeBaseUrl(confluenceBaseUrl) || void 0,
|
|
4566
4875
|
apiToken: confluenceApiToken || void 0
|
|
4567
4876
|
}
|
|
4568
4877
|
} : {},
|
|
4569
4878
|
...useArtifactory ? {
|
|
4570
4879
|
artifactory: {
|
|
4571
|
-
baseUrl: artifactoryBaseUrl || void 0,
|
|
4880
|
+
baseUrl: normalizeBaseUrl(artifactoryBaseUrl) || void 0,
|
|
4572
4881
|
token: artifactoryToken || void 0,
|
|
4573
4882
|
npmRepo: npmRepo || void 0,
|
|
4574
4883
|
nugetRepo: nugetRepo || void 0,
|
|
@@ -4577,7 +4886,7 @@ async function initGlobalConfig(start) {
|
|
|
4577
4886
|
} : {},
|
|
4578
4887
|
...useSonar ? {
|
|
4579
4888
|
sonar: {
|
|
4580
|
-
baseUrl: sonarBaseUrl || void 0,
|
|
4889
|
+
baseUrl: normalizeBaseUrl(sonarBaseUrl) || void 0,
|
|
4581
4890
|
token: sonarToken || void 0
|
|
4582
4891
|
}
|
|
4583
4892
|
} : {},
|
|
@@ -4588,7 +4897,7 @@ async function initGlobalConfig(start) {
|
|
|
4588
4897
|
} : {},
|
|
4589
4898
|
...useAdo && adoBaseUrl ? {
|
|
4590
4899
|
ado: {
|
|
4591
|
-
baseUrl: adoBaseUrl,
|
|
4900
|
+
baseUrl: normalizeBaseUrl(adoBaseUrl),
|
|
4592
4901
|
...adoPat ? { pat: adoPat } : {},
|
|
4593
4902
|
...Object.keys(adoFieldAliases).length ? { fieldAliases: adoFieldAliases } : {},
|
|
4594
4903
|
...adoDiscoveredFields.length ? { discoveredFields: adoDiscoveredFields } : {},
|
|
@@ -4597,14 +4906,14 @@ async function initGlobalConfig(start) {
|
|
|
4597
4906
|
} : {},
|
|
4598
4907
|
...useJenkins && jenkinsBaseUrl ? {
|
|
4599
4908
|
jenkins: {
|
|
4600
|
-
baseUrl: jenkinsBaseUrl,
|
|
4909
|
+
baseUrl: normalizeBaseUrl(jenkinsBaseUrl),
|
|
4601
4910
|
...jenkinsUsername ? { username: jenkinsUsername } : {},
|
|
4602
4911
|
...jenkinsApiToken ? { apiToken: jenkinsApiToken } : {}
|
|
4603
4912
|
}
|
|
4604
4913
|
} : {},
|
|
4605
4914
|
...useUdeploy && udeployBaseUrl ? {
|
|
4606
4915
|
udeploy: {
|
|
4607
|
-
baseUrl: udeployBaseUrl,
|
|
4916
|
+
baseUrl: normalizeBaseUrl(udeployBaseUrl),
|
|
4608
4917
|
...udeployPat ? { pat: udeployPat } : {},
|
|
4609
4918
|
...udeployUsername ? { username: udeployUsername } : {},
|
|
4610
4919
|
...udeployPassword ? { password: udeployPassword } : {}
|
|
@@ -4612,14 +4921,14 @@ async function initGlobalConfig(start) {
|
|
|
4612
4921
|
} : {},
|
|
4613
4922
|
...useCheckmarx && checkmarxBaseUrl ? {
|
|
4614
4923
|
checkmarx: {
|
|
4615
|
-
baseUrl: checkmarxBaseUrl,
|
|
4924
|
+
baseUrl: normalizeBaseUrl(checkmarxBaseUrl),
|
|
4616
4925
|
username: checkmarxUsername || void 0,
|
|
4617
4926
|
password: checkmarxPassword || void 0
|
|
4618
4927
|
}
|
|
4619
4928
|
} : {},
|
|
4620
4929
|
...useServiceNow && servicenowBaseUrl ? {
|
|
4621
4930
|
servicenow: {
|
|
4622
|
-
baseUrl: servicenowBaseUrl,
|
|
4931
|
+
baseUrl: normalizeBaseUrl(servicenowBaseUrl),
|
|
4623
4932
|
username: servicenowUsername || void 0,
|
|
4624
4933
|
...servicenowApiToken ? { apiToken: servicenowApiToken } : {},
|
|
4625
4934
|
...servicenowPassword ? { password: servicenowPassword } : {}
|
|
@@ -4627,13 +4936,20 @@ async function initGlobalConfig(start) {
|
|
|
4627
4936
|
} : {},
|
|
4628
4937
|
...useContrast && contrastOrgUuid ? {
|
|
4629
4938
|
contrast: {
|
|
4630
|
-
...contrastBaseUrl ? { baseUrl: contrastBaseUrl } : {},
|
|
4939
|
+
...contrastBaseUrl ? { baseUrl: normalizeBaseUrl(contrastBaseUrl) } : {},
|
|
4631
4940
|
orgUuid: contrastOrgUuid,
|
|
4632
4941
|
username: contrastUsername || void 0,
|
|
4633
4942
|
apiKey: contrastApiKey || void 0,
|
|
4634
4943
|
serviceKey: contrastServiceKey || void 0
|
|
4635
4944
|
}
|
|
4636
4945
|
} : {},
|
|
4946
|
+
...useSonatypeIq && sonatypeIqBaseUrl ? {
|
|
4947
|
+
sonatypeiq: {
|
|
4948
|
+
baseUrl: normalizeBaseUrl(sonatypeIqBaseUrl),
|
|
4949
|
+
userCode: sonatypeIqUserCode || void 0,
|
|
4950
|
+
passcode: sonatypeIqPasscode || void 0
|
|
4951
|
+
}
|
|
4952
|
+
} : {},
|
|
4637
4953
|
defaults: {
|
|
4638
4954
|
jira: {
|
|
4639
4955
|
project: jiraProject || void 0
|
|
@@ -5538,11 +5854,11 @@ function registerAdoRepoCommands(ado) {
|
|
|
5538
5854
|
const start = Date.now();
|
|
5539
5855
|
try {
|
|
5540
5856
|
const globalOpts = ado.optsWithGlobals();
|
|
5541
|
-
const { loadConfig: lc } = await import("./config-
|
|
5857
|
+
const { loadConfig: lc } = await import("./config-NCI2BTQG.js");
|
|
5542
5858
|
const config = lc({ configPath: globalOpts.config });
|
|
5543
5859
|
const { collection, project, repo: repo2, gitClient } = getAdoContext(ado, true);
|
|
5544
5860
|
const { AdoCoreClient: AdoCoreClient2 } = await import("./core-SWYVXOQ4.js");
|
|
5545
|
-
const { createHttpClient: createHttpClient2 } = await import("./http-
|
|
5861
|
+
const { createHttpClient: createHttpClient2 } = await import("./http-L2P23H2D.js");
|
|
5546
5862
|
const http = createHttpClient2(config, Boolean(globalOpts.dryRun));
|
|
5547
5863
|
const coreClient = new AdoCoreClient2(http);
|
|
5548
5864
|
const me = await coreClient.getConnectionData(collection);
|
|
@@ -6154,7 +6470,7 @@ var UdeployClient = class {
|
|
|
6154
6470
|
};
|
|
6155
6471
|
|
|
6156
6472
|
// src/services/udeploy/commands.ts
|
|
6157
|
-
var
|
|
6473
|
+
var POLL_INTERVAL_MS2 = 5e3;
|
|
6158
6474
|
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
6159
6475
|
function getClient8(program2) {
|
|
6160
6476
|
const opts = program2.optsWithGlobals();
|
|
@@ -6265,7 +6581,7 @@ function registerUdeployCommands(program2) {
|
|
|
6265
6581
|
if (elapsed >= timeoutMs) {
|
|
6266
6582
|
throw new PncliError(`Timed out after ${timeoutMs}ms waiting for request ${requestId}`, 1);
|
|
6267
6583
|
}
|
|
6268
|
-
await sleep(
|
|
6584
|
+
await sleep(POLL_INTERVAL_MS2);
|
|
6269
6585
|
const status = await client.getRequestStatus(requestId);
|
|
6270
6586
|
if (status.status === "CLOSED") {
|
|
6271
6587
|
const outcome = { requestId, status: status.status, result: status.result, elapsed_ms: Date.now() - start };
|
|
@@ -6584,19 +6900,87 @@ function registerContrastCommands(program2) {
|
|
|
6584
6900
|
});
|
|
6585
6901
|
}
|
|
6586
6902
|
|
|
6903
|
+
// src/services/sonatypeiq/commands.ts
|
|
6904
|
+
function getHttpClient(program2) {
|
|
6905
|
+
const opts = program2.optsWithGlobals();
|
|
6906
|
+
const config = loadConfig({ configPath: opts.config });
|
|
6907
|
+
if (!config.sonatypeiq.baseUrl) throw new PncliError("Sonatype IQ Server baseUrl not configured. Run: pncli config init");
|
|
6908
|
+
if (!config.sonatypeiq.userCode || !config.sonatypeiq.passcode) throw new PncliError("Sonatype IQ Server credentials not configured. Run: pncli config init");
|
|
6909
|
+
const http = createHttpClient(config, Boolean(opts.dryRun));
|
|
6910
|
+
return http;
|
|
6911
|
+
}
|
|
6912
|
+
function registerSonatypeIqCommands(program2) {
|
|
6913
|
+
const sonatypeiq = program2.command("sonatypeiq").description("Sonatype IQ Server operations");
|
|
6914
|
+
const applications = sonatypeiq.command("applications").description("Application operations");
|
|
6915
|
+
applications.command("list").description("List all applications in Sonatype IQ Server").option("--organization-id <id>", "Filter by organization ID").action(async (opts) => {
|
|
6916
|
+
const startTime = Date.now();
|
|
6917
|
+
try {
|
|
6918
|
+
const http = getHttpClient(program2);
|
|
6919
|
+
const params = {};
|
|
6920
|
+
if (opts.organizationId) params["organizationId"] = opts.organizationId;
|
|
6921
|
+
const data = await http.sonatypeiq("/api/v2/applications", { params });
|
|
6922
|
+
success(data, "sonatypeiq", "applications list", startTime);
|
|
6923
|
+
} catch (err) {
|
|
6924
|
+
fail(err, "sonatypeiq", "applications list", startTime);
|
|
6925
|
+
}
|
|
6926
|
+
});
|
|
6927
|
+
applications.command("get").description("Get an application by its public ID").requiredOption("--public-id <id>", "Application public ID").action(async (opts) => {
|
|
6928
|
+
const startTime = Date.now();
|
|
6929
|
+
try {
|
|
6930
|
+
const http = getHttpClient(program2);
|
|
6931
|
+
const data = await http.sonatypeiq("/api/v2/applications", {
|
|
6932
|
+
params: { publicId: opts.publicId }
|
|
6933
|
+
});
|
|
6934
|
+
success(data, "sonatypeiq", "applications get", startTime);
|
|
6935
|
+
} catch (err) {
|
|
6936
|
+
fail(err, "sonatypeiq", "applications get", startTime);
|
|
6937
|
+
}
|
|
6938
|
+
});
|
|
6939
|
+
const organizations = sonatypeiq.command("organizations").description("Organization operations");
|
|
6940
|
+
organizations.command("list").description("List all organizations in Sonatype IQ Server").action(async () => {
|
|
6941
|
+
const startTime = Date.now();
|
|
6942
|
+
try {
|
|
6943
|
+
const http = getHttpClient(program2);
|
|
6944
|
+
const data = await http.sonatypeiq("/api/v2/organizations");
|
|
6945
|
+
success(data, "sonatypeiq", "organizations list", startTime);
|
|
6946
|
+
} catch (err) {
|
|
6947
|
+
fail(err, "sonatypeiq", "organizations list", startTime);
|
|
6948
|
+
}
|
|
6949
|
+
});
|
|
6950
|
+
const policies = sonatypeiq.command("policies").description("Policy operations");
|
|
6951
|
+
policies.command("list").description("List all policies").option("--organization-id <id>", "Filter by organization ID").action(async (opts) => {
|
|
6952
|
+
const startTime = Date.now();
|
|
6953
|
+
try {
|
|
6954
|
+
const http = getHttpClient(program2);
|
|
6955
|
+
const path6 = opts.organizationId ? `/api/v2/policies/organization/${encodeURIComponent(opts.organizationId)}` : "/api/v2/policies";
|
|
6956
|
+
const data = await http.sonatypeiq(path6);
|
|
6957
|
+
success(data, "sonatypeiq", "policies list", startTime);
|
|
6958
|
+
} catch (err) {
|
|
6959
|
+
fail(err, "sonatypeiq", "policies list", startTime);
|
|
6960
|
+
}
|
|
6961
|
+
});
|
|
6962
|
+
}
|
|
6963
|
+
|
|
6587
6964
|
// src/services/skills/commands.ts
|
|
6588
6965
|
import fs3 from "fs";
|
|
6589
6966
|
import path5 from "path";
|
|
6590
6967
|
import os from "os";
|
|
6591
|
-
|
|
6592
|
-
var RAW_BASE = "https://raw.githubusercontent.com/kolatts/pncli/main/skills";
|
|
6968
|
+
import { fileURLToPath } from "url";
|
|
6593
6969
|
var AGENT_PATHS = {
|
|
6594
6970
|
"github-copilot": { project: ".agents/skills", user: path5.join(os.homedir(), ".copilot/skills") },
|
|
6595
6971
|
"claude-code": { project: ".claude/skills", user: path5.join(os.homedir(), ".claude/skills") }
|
|
6596
6972
|
};
|
|
6973
|
+
function getBundledSkillsDir() {
|
|
6974
|
+
try {
|
|
6975
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
6976
|
+
return path5.resolve(path5.dirname(thisFile), "..", "skills");
|
|
6977
|
+
} catch {
|
|
6978
|
+
return "";
|
|
6979
|
+
}
|
|
6980
|
+
}
|
|
6597
6981
|
function registerSkillsCommands(program2) {
|
|
6598
6982
|
const skills = program2.command("skills").description("Manage pncli Claude Code skills");
|
|
6599
|
-
skills.command("install").description("
|
|
6983
|
+
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
6984
|
const start = Date.now();
|
|
6601
6985
|
try {
|
|
6602
6986
|
let targetDir;
|
|
@@ -6610,19 +6994,22 @@ function registerSkillsCommands(program2) {
|
|
|
6610
6994
|
const scopePath = opts.scope === "user" ? agentConfig.user : agentConfig.project;
|
|
6611
6995
|
targetDir = path5.resolve(scopePath);
|
|
6612
6996
|
}
|
|
6613
|
-
|
|
6614
|
-
const
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6997
|
+
const resolvedTarget = path5.resolve(targetDir);
|
|
6998
|
+
const bundledDir = getBundledSkillsDir();
|
|
6999
|
+
let skillDirs = [];
|
|
7000
|
+
if (bundledDir !== "") {
|
|
7001
|
+
try {
|
|
7002
|
+
skillDirs = fs3.readdirSync(bundledDir).filter(
|
|
7003
|
+
(name) => fs3.existsSync(path5.join(bundledDir, name, "SKILL.md"))
|
|
7004
|
+
);
|
|
7005
|
+
} catch {
|
|
7006
|
+
}
|
|
6619
7007
|
}
|
|
6620
|
-
const entries = await listRes.json();
|
|
6621
|
-
const skillDirs = entries.filter((e) => e.type === "dir").map((e) => e.name);
|
|
6622
7008
|
if (skillDirs.length === 0) {
|
|
6623
|
-
throw new Error("No skills found
|
|
7009
|
+
throw new Error("No bundled skills found. Reinstall pncli to get the latest version: npm install -g @kolatts/pncli");
|
|
6624
7010
|
}
|
|
6625
|
-
const
|
|
7011
|
+
const installed = [];
|
|
7012
|
+
const failed = [];
|
|
6626
7013
|
for (const skillName of skillDirs) {
|
|
6627
7014
|
const existingDir = path5.resolve(targetDir, skillName);
|
|
6628
7015
|
if (!existingDir.startsWith(resolvedTarget + path5.sep)) continue;
|
|
@@ -6630,25 +7017,15 @@ function registerSkillsCommands(program2) {
|
|
|
6630
7017
|
fs3.rmSync(existingDir, { recursive: true, force: true });
|
|
6631
7018
|
}
|
|
6632
7019
|
}
|
|
6633
|
-
warn(`
|
|
6634
|
-
const installed = [];
|
|
6635
|
-
const failed = [];
|
|
7020
|
+
warn(`Installing ${skillDirs.length} bundled skill(s) to ${targetDir}...`);
|
|
6636
7021
|
for (const skillName of skillDirs) {
|
|
6637
7022
|
const skillDir = path5.resolve(targetDir, skillName);
|
|
6638
7023
|
if (!skillDir.startsWith(resolvedTarget + path5.sep)) {
|
|
6639
7024
|
failed.push(skillName);
|
|
6640
7025
|
continue;
|
|
6641
7026
|
}
|
|
6642
|
-
const skillUrl = `${RAW_BASE}/${skillName}/SKILL.md`;
|
|
6643
7027
|
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();
|
|
7028
|
+
const content = fs3.readFileSync(path5.join(bundledDir, skillName, "SKILL.md"), "utf8");
|
|
6652
7029
|
fs3.mkdirSync(skillDir, { recursive: true });
|
|
6653
7030
|
fs3.writeFileSync(path5.join(skillDir, "SKILL.md"), content, "utf8");
|
|
6654
7031
|
installed.push(skillName);
|
|
@@ -6658,7 +7035,7 @@ function registerSkillsCommands(program2) {
|
|
|
6658
7035
|
}
|
|
6659
7036
|
warn(`Installed ${installed.length} skill(s) to ${targetDir}`);
|
|
6660
7037
|
if (failed.length > 0) {
|
|
6661
|
-
warn(`Failed to
|
|
7038
|
+
warn(`Failed to install: ${failed.join(", ")}`);
|
|
6662
7039
|
}
|
|
6663
7040
|
success({
|
|
6664
7041
|
installed,
|
|
@@ -6666,7 +7043,8 @@ function registerSkillsCommands(program2) {
|
|
|
6666
7043
|
target: targetDir,
|
|
6667
7044
|
total: installed.length,
|
|
6668
7045
|
agent: opts.target ? "custom" : opts.agent,
|
|
6669
|
-
scope: opts.target ? "custom" : opts.scope
|
|
7046
|
+
scope: opts.target ? "custom" : opts.scope,
|
|
7047
|
+
source: "bundled"
|
|
6670
7048
|
}, "skills", "install", start);
|
|
6671
7049
|
} catch (err) {
|
|
6672
7050
|
fail(err, "skills", "install", start);
|
|
@@ -6772,6 +7150,7 @@ registerUdeployCommands(program);
|
|
|
6772
7150
|
registerCheckmarxCommands(program);
|
|
6773
7151
|
registerServiceNowCommands(program);
|
|
6774
7152
|
registerContrastCommands(program);
|
|
7153
|
+
registerSonatypeIqCommands(program);
|
|
6775
7154
|
registerSkillsCommands(program);
|
|
6776
7155
|
program.addHelpText("after", `
|
|
6777
7156
|
Services:
|
|
@@ -6789,6 +7168,7 @@ Services:
|
|
|
6789
7168
|
checkmarx Checkmarx CxSAST (projects, scans, scan statistics)
|
|
6790
7169
|
servicenow ServiceNow (change requests)
|
|
6791
7170
|
contrast Contrast IAST (applications, vulnerability findings)
|
|
7171
|
+
sonatypeiq Sonatype IQ Server (applications, organizations, policies)
|
|
6792
7172
|
config Manage pncli configuration
|
|
6793
7173
|
skills Download and manage Claude Code skills
|
|
6794
7174
|
`);
|