harness-mcp-v2 3.2.39 → 3.2.40

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.
Files changed (54) hide show
  1. package/README.md +9 -3
  2. package/build/client/harness-client.d.ts +1 -0
  3. package/build/client/harness-client.d.ts.map +1 -1
  4. package/build/client/harness-client.js +29 -7
  5. package/build/client/harness-client.js.map +1 -1
  6. package/build/client/types.d.ts +2 -2
  7. package/build/client/types.d.ts.map +1 -1
  8. package/build/config.d.ts +25 -1
  9. package/build/config.d.ts.map +1 -1
  10. package/build/config.js +47 -0
  11. package/build/config.js.map +1 -1
  12. package/build/index.js +1 -1
  13. package/build/index.js.map +1 -1
  14. package/build/registry/appsec/builders.d.ts +33 -0
  15. package/build/registry/appsec/builders.d.ts.map +1 -0
  16. package/build/registry/appsec/builders.js +1403 -0
  17. package/build/registry/appsec/builders.js.map +1 -0
  18. package/build/registry/appsec/extractors.d.ts +46 -0
  19. package/build/registry/appsec/extractors.d.ts.map +1 -0
  20. package/build/registry/appsec/extractors.js +699 -0
  21. package/build/registry/appsec/extractors.js.map +1 -0
  22. package/build/registry/appsec/queries.d.ts +22 -0
  23. package/build/registry/appsec/queries.d.ts.map +1 -0
  24. package/build/registry/appsec/queries.js +322 -0
  25. package/build/registry/appsec/queries.js.map +1 -0
  26. package/build/registry/extractors.d.ts +2 -1
  27. package/build/registry/extractors.d.ts.map +1 -1
  28. package/build/registry/extractors.js +2 -1
  29. package/build/registry/extractors.js.map +1 -1
  30. package/build/registry/index.d.ts.map +1 -1
  31. package/build/registry/index.js +3 -1
  32. package/build/registry/index.js.map +1 -1
  33. package/build/registry/toolsets/appsec.d.ts +5 -0
  34. package/build/registry/toolsets/appsec.d.ts.map +1 -0
  35. package/build/registry/toolsets/appsec.js +624 -0
  36. package/build/registry/toolsets/appsec.js.map +1 -0
  37. package/build/registry/toolsets/chaos-descriptions.d.ts +12 -11
  38. package/build/registry/toolsets/chaos-descriptions.d.ts.map +1 -1
  39. package/build/registry/toolsets/chaos-descriptions.js +15 -13
  40. package/build/registry/toolsets/chaos-descriptions.js.map +1 -1
  41. package/build/registry/toolsets/chaos.d.ts.map +1 -1
  42. package/build/registry/toolsets/chaos.js +90 -32
  43. package/build/registry/toolsets/chaos.js.map +1 -1
  44. package/build/registry/toolsets/runbooks.d.ts.map +1 -1
  45. package/build/registry/toolsets/runbooks.js +6 -2
  46. package/build/registry/toolsets/runbooks.js.map +1 -1
  47. package/build/registry/types.d.ts +10 -2
  48. package/build/registry/types.d.ts.map +1 -1
  49. package/build/utils/session-headers.d.ts +1 -0
  50. package/build/utils/session-headers.d.ts.map +1 -1
  51. package/build/utils/session-headers.js +6 -1
  52. package/build/utils/session-headers.js.map +1 -1
  53. package/npm-shrinkwrap.json +2 -2
  54. package/package.json +1 -1
@@ -22,7 +22,7 @@ descExperimentManifest, descExperimentInfraType, descExperimentInfraIdCreate, de
22
22
  // Service Discovery
23
23
  descSDAgentIdentity, descSDEnvironmentId, descSDAgentListEnvironmentId, descSDFetchAll, descSDAgentDiagnostic, descDiscoveredAgent, descListDiscoveredAgents, descDiscoveredAgentSearch, descDiscoveredNamespace, descListDiscoveredNamespaces, descSDNamespaceNameFilter, descDiscoveredService, descListDiscoveredServices, descSDNamespaceFilter, descSDSearchFilter,
24
24
  // Scanned Risks / Risk Rules / Risk Scans (chaos-manager v3)
25
- descScannedRisk, descChaosRiskRule, descChaosRiskScan, descListScannedRisks, descGetScannedRisk, descListScannedRiskOccurrences, descSummarizeScannedRisksByService, descListRiskRules, descGetRiskRule, descCreateRiskRule, descUpdateRiskRule, descDeleteRiskRule, descBodyRiskRuleCreate, descBodyRiskRuleUpdate, descRiskRuleIdentity, descRiskRuleName, descRiskRuleDescription, descRiskRuleTags, descRiskRuleSeverity, descRiskRuleCategory, descRiskRuleSpec, descRiskRuleCustomOnly, descListRiskScans, descGetRiskScan, descCreateRiskScan, descUpdateRiskScan, descDeleteRiskScan, descRetryRiskScan, descAbortRiskScan, descSubmitRiskScanFindings, descBodySubmitRiskScanFindings, descRiskScanFindings, descRiskScanDetectionMethod, descGetRiskScanReport, descGetRiskScanReportDownload, descGetRiskScanHeatmap, descBodyRiskScanCreate, descBodyRiskScanUpdate, descRiskScanIdentity, descRiskScanName, descRiskScanDescription, descRiskScanTags, descRiskScanType, descRiskScanSource, descScannedRiskSeverity, descScannedRiskRuleId, descScannedRiskValidationType, descScannedRiskServiceIdentity, descScannedRiskScanType, descRiskEnvironmentIdentity, descRiskAgentIdentity, descRiskPipelineIdentity, descRiskScanStatus, descRiskSearch, descRiskSortField, descRiskSortAscending, descRiskTags, descRiskIncludeAllScope, descRiskStartTime, descRiskEndTime, descRiskRuleDataSource, descRiskRuleIsSystem, descHeatmapSearch, } from "./chaos-descriptions.js";
25
+ descScannedRisk, descChaosRiskRule, descChaosRiskScan, descListScannedRisks, descGetScannedRisk, descListScannedRiskOccurrences, descSummarizeScannedRisksByService, descListRiskRules, descGetRiskRule, descCreateRiskRule, descUpdateRiskRule, descDeleteRiskRule, descBodyRiskRuleCreate, descBodyRiskRuleUpdate, descRiskRuleIdentity, descRiskRuleName, descRiskRuleDescription, descRiskRuleTags, descRiskRuleSeverity, descRiskRuleCategory, descRiskRuleSpec, descRiskRuleCustomOnly, descRiskRuleIncludeInPassiveScan, descListRiskScans, descGetRiskScan, descCreateRiskScan, descUpdateRiskScan, descDeleteRiskScan, descRetryRiskScan, descAbortRiskScan, descSubmitRiskScanFindings, descBodySubmitRiskScanFindings, descRiskScanFindings, descRiskScanDetectionMethod, descGetRiskScanReport, descGetRiskScanReportDownload, descGetRiskScanHeatmap, descBodyRiskScanCreate, descBodyRiskScanUpdate, descRiskScanIdentity, descRiskScanName, descRiskScanDescription, descRiskScanTags, descRiskScanType, descRiskScanSource, descScannedRiskSeverity, descScannedRiskRuleId, descScannedRiskValidationType, descScannedRiskServiceIdentity, descScannedRiskScanType, descRiskEnvironmentIdentity, descRiskAgentIdentity, descRiskPipelineIdentity, descRiskScanStatus, descRiskSearch, descRiskSortField, descRiskSortAscending, descRiskTags, descRiskIncludeAllScope, descRiskStartTime, descRiskEndTime, descRiskRuleDataSource, descRiskRuleIsSystem, descHeatmapSearch, } from "./chaos-descriptions.js";
26
26
  /**
27
27
  * Chaos API base path.
28
28
  * REST endpoints live under rest/v2/ (experiments, probes) and rest/ (templates).
@@ -124,15 +124,33 @@ function stringList(value) {
124
124
  return value.map((item) => String(item).trim()).filter(Boolean);
125
125
  return String(value).split(",").map((item) => item.trim()).filter(Boolean);
126
126
  }
127
+ const LINKED_DISCOVERED_SERVICE_KEYS = new Set([
128
+ "agent_id", "agentId",
129
+ "environment_id", "environmentId",
130
+ "external_id", "externalId",
131
+ ]);
132
+ function valueIsSet(value) {
133
+ if (value == null)
134
+ return false;
135
+ if (typeof value === "string")
136
+ return value.trim() !== "";
137
+ return true;
138
+ }
139
+ // "absent" leaves the stored link unchanged. "clear" is {} or the three known
140
+ // id fields all blank, with no other key set, and removes the link on update.
141
+ // A discovered-service record or a typo such as agentID is not a clear.
127
142
  function linkedDiscoveredService(value) {
128
- if (!value || typeof value !== "object" || Array.isArray(value))
129
- return undefined;
143
+ if (value == null)
144
+ return "absent";
145
+ if (typeof value !== "object" || Array.isArray(value))
146
+ return "absent";
130
147
  const linked = value;
131
148
  const agentId = optionalText(linked.agent_id ?? linked.agentId);
132
149
  const environmentId = optionalText(linked.environment_id ?? linked.environmentId);
133
150
  const externalId = optionalText(linked.external_id ?? linked.externalId);
134
- if (!agentId && !environmentId && !externalId)
135
- return undefined;
151
+ const unrecognized = Object.keys(linked).some((key) => !LINKED_DISCOVERED_SERVICE_KEYS.has(key) && valueIsSet(linked[key]));
152
+ if (!agentId && !environmentId && !externalId && !unrecognized)
153
+ return "clear";
136
154
  if (!agentId || !environmentId || !externalId) {
137
155
  const missing = [
138
156
  !agentId ? "agent_id" : "",
@@ -143,6 +161,14 @@ function linkedDiscoveredService(value) {
143
161
  }
144
162
  return { agentId, environmentId, externalId };
145
163
  }
164
+ function linkedDiscoveredServiceBody(value, allowClear) {
165
+ const linked = linkedDiscoveredService(value);
166
+ if (linked === "absent" || (linked === "clear" && !allowClear))
167
+ return {};
168
+ if (linked === "clear")
169
+ return { linkedDiscoveredService: {} };
170
+ return { linkedDiscoveredService: linked };
171
+ }
146
172
  /**
147
173
  * A CodeRepository chaos service is the only one that may omit infrastructure,
148
174
  * and only when agent_id is the custom discovery agent. Every other service
@@ -242,19 +268,26 @@ const RISK_RULE_BODY_FIELDS = [
242
268
  { name: "category", type: "string", required: true, description: descRiskRuleCategory },
243
269
  { name: "spec", type: "object", required: true, description: descRiskRuleSpec },
244
270
  ];
245
- const RISK_RULE_IDENTITY_RE = /^code-[a-z0-9-]{3,60}$/;
246
- const RISK_RULE_DATA_SOURCE = "SourceCode";
271
+ const RISK_RULE_DATA_SOURCES = ["SourceCode", "ApplicationManifest"];
272
+ const DEFAULT_RISK_RULE_DATA_SOURCE = "SourceCode";
273
+ const RISK_RULE_IDENTITY_RE = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
247
274
  const RISK_RULE_PROMPT_LIST_LIMIT = 10;
248
275
  const RISK_RULE_PROMPT_ENTRY_LIMIT = 500;
249
- /** Custom rules are SourceCode-only; an omitted dataSource defaults to that. */
250
- function riskRuleDataSource(value) {
251
- const dataSource = value ?? [RISK_RULE_DATA_SOURCE];
252
- if (!Array.isArray(dataSource) ||
253
- dataSource.length !== 1 ||
254
- dataSource[0] !== RISK_RULE_DATA_SOURCE) {
255
- throw new Error('spec.dataSource must be exactly ["SourceCode"]. Custom rules are SourceCode-only.');
276
+ function isRiskRuleDataSource(value) {
277
+ return typeof value === "string" && RISK_RULE_DATA_SOURCES.includes(value);
278
+ }
279
+ /**
280
+ * Validates spec.dataSource. Create passes defaultWhenOmitted so a missing
281
+ * value becomes SourceCode. Update does not: an omitted value is rejected
282
+ * so an ApplicationManifest rule is not rewritten to SourceCode.
283
+ */
284
+ function riskRuleDataSource(value, defaultWhenOmitted) {
285
+ const dataSource = value ?? (defaultWhenOmitted ? [DEFAULT_RISK_RULE_DATA_SOURCE] : undefined);
286
+ const source = Array.isArray(dataSource) && dataSource.length === 1 ? dataSource[0] : undefined;
287
+ if (!isRiskRuleDataSource(source)) {
288
+ throw new Error('spec.dataSource must be exactly ["SourceCode"] or ["ApplicationManifest"].');
256
289
  }
257
- return dataSource;
290
+ return [source];
258
291
  }
259
292
  /**
260
293
  * prompt.do and prompt.outcome are required; prompt.dont is optional.
@@ -284,8 +317,8 @@ function riskRulePromptList(value, field, required) {
284
317
  }
285
318
  }
286
319
  }
287
- /** Builds the mutable part of a custom risk rule; spec.dataSource defaults to SourceCode. */
288
- function buildRiskRuleBody(b) {
320
+ /** Builds the mutable part of a custom risk rule. defaultDataSource is create-only. */
321
+ function buildRiskRuleBody(b, options = {}) {
289
322
  const missing = ["name", "severity", "category", "spec"].filter((k) => b[k] == null || b[k] === "");
290
323
  if (missing.length > 0) {
291
324
  throw new Error(`Missing required field(s): ${missing.join(", ")}.`);
@@ -293,8 +326,8 @@ function buildRiskRuleBody(b) {
293
326
  if (typeof b.spec !== "object" || b.spec == null || Array.isArray(b.spec)) {
294
327
  throw new Error("spec must be an object.");
295
328
  }
296
- const spec = b.spec;
297
- const dataSource = riskRuleDataSource(spec.dataSource);
329
+ const spec = { ...b.spec };
330
+ const dataSource = riskRuleDataSource(spec.dataSource, options.defaultDataSource === true);
298
331
  if (!Array.isArray(spec.faults) || spec.faults.length === 0) {
299
332
  throw new Error("spec.faults must list at least one chaos fault that can prove the risk (e.g. pod-network-latency).");
300
333
  }
@@ -305,6 +338,15 @@ function buildRiskRuleBody(b) {
305
338
  riskRulePromptList(prompt.do, "spec.prompt.do", true);
306
339
  riskRulePromptList(prompt.dont, "spec.prompt.dont", false);
307
340
  riskRulePromptList(prompt.outcome, "spec.prompt.outcome", true);
341
+ // The list filter is include_in_passive_scan. Accept that key inside spec and
342
+ // send the camelCase field the API stores. Null is not a boolean.
343
+ if ("include_in_passive_scan" in spec) {
344
+ spec.includeInPassiveScan = spec.include_in_passive_scan;
345
+ delete spec.include_in_passive_scan;
346
+ }
347
+ if (spec.includeInPassiveScan !== undefined && typeof spec.includeInPassiveScan !== "boolean") {
348
+ throw new Error("spec.includeInPassiveScan must be a boolean. Omit it to leave the rule out of the passive scan.");
349
+ }
308
350
  return {
309
351
  name: b.name,
310
352
  ...(b.description != null ? { description: b.description } : {}),
@@ -2496,7 +2538,7 @@ export const chaosToolset = {
2496
2538
  const infrastructureType = optionalText(b.infrastructure_type ?? b.infrastructureType);
2497
2539
  const onboardingId = optionalText(b.onboarding_id ?? b.onboardingId);
2498
2540
  const tags = stringList(b.tags);
2499
- const linked = linkedDiscoveredService(b.linked_discovered_service ?? b.linkedDiscoveredService);
2541
+ const linked = linkedDiscoveredServiceBody(b.linked_discovered_service ?? b.linkedDiscoveredService, false);
2500
2542
  assertChaosServiceInfrastructure(agentId, environmentId, infrastructureId);
2501
2543
  const probes = b.probes?.map((p) => ({
2502
2544
  probeId: p.probeId ?? p.probe_id,
@@ -2520,7 +2562,7 @@ export const chaosToolset = {
2520
2562
  ...(infrastructureId ? { infrastructureId, infrastructure_id: infrastructureId } : {}),
2521
2563
  ...(infrastructureType ? { infrastructureType } : {}),
2522
2564
  ...(onboardingId ? { onboardingId } : {}),
2523
- ...(linked ? { linkedDiscoveredService: linked } : {}),
2565
+ ...linked,
2524
2566
  ...(probes && probes.length > 0 ? { probes } : {}),
2525
2567
  };
2526
2568
  },
@@ -2553,11 +2595,20 @@ export const chaosToolset = {
2553
2595
  const b = coerceBody(input);
2554
2596
  const externalServiceId = b.external_service_id ?? b.externalServiceId;
2555
2597
  const agentId = b.agent_id ?? b.agentId;
2556
- const environmentId = optionalText(b.environment_id ?? b.environmentId);
2557
- const infrastructureId = optionalText(b.infrastructure_id ?? b.infrastructureId);
2558
- const infrastructureType = optionalText(b.infrastructure_type ?? b.infrastructureType);
2559
2598
  const tags = stringList(b.tags);
2560
- const linked = linkedDiscoveredService(b.linked_discovered_service ?? b.linkedDiscoveredService);
2599
+ // A present linkedDiscoveredService is validated for a CodeRepository
2600
+ // service. An empty object is an unlink on every service type, so a
2601
+ // Kubernetes update must not send it. Only the custom discovery agent
2602
+ // may clear the link.
2603
+ const linked = linkedDiscoveredServiceBody(b.linked_discovered_service ?? b.linkedDiscoveredService, optionalText(agentId) === CUSTOM_DISCOVERY_AGENT);
2604
+ // hce-saas stores the environment and infrastructure sent with an
2605
+ // unlink, so values resupplied from harness_get would keep the
2606
+ // workload's Kubernetes mapping on the code-repository service.
2607
+ const clearsLink = linked.linkedDiscoveredService !== undefined
2608
+ && Object.keys(linked.linkedDiscoveredService).length === 0;
2609
+ const environmentId = clearsLink ? undefined : optionalText(b.environment_id ?? b.environmentId);
2610
+ const infrastructureId = clearsLink ? undefined : optionalText(b.infrastructure_id ?? b.infrastructureId);
2611
+ const infrastructureType = clearsLink ? undefined : optionalText(b.infrastructure_type ?? b.infrastructureType);
2561
2612
  assertChaosServiceInfrastructure(optionalText(agentId), environmentId, infrastructureId);
2562
2613
  const probes = b.probes?.map((p) => ({
2563
2614
  probeId: p.probeId ?? p.probe_id,
@@ -2586,7 +2637,7 @@ export const chaosToolset = {
2586
2637
  ...(environmentId ? { environmentId, environment_id: environmentId } : {}),
2587
2638
  ...(infrastructureId ? { infrastructureId, infrastructure_id: infrastructureId } : {}),
2588
2639
  ...(infrastructureType ? { infrastructureType } : {}),
2589
- ...(linked ? { linkedDiscoveredService: linked } : {}),
2640
+ ...linked,
2590
2641
  // Full desired-state replace (hce-saas reconcileProbeMappings):
2591
2642
  // pass an explicit [] straight through (detach all) rather than
2592
2643
  // dropping it, but leave the key absent when the caller omitted it
@@ -4523,7 +4574,9 @@ export const chaosToolset = {
4523
4574
  displayName: "Chaos Risk Rule",
4524
4575
  description: descChaosRiskRule,
4525
4576
  toolset: "chaos",
4526
- scope: "project",
4577
+ // Custom rules are stored on the account. Org and project must not be sent,
4578
+ // or the API stores and lists them as project rules.
4579
+ scope: "account",
4527
4580
  scopeParams: CHAOS_SCOPE,
4528
4581
  identifierFields: ["identity"],
4529
4582
  searchAliases: ["risk rule", "risk check", "manifest risk rule", "code risk rule", "source code risk rule", "umbrella risk rule"],
@@ -4535,7 +4588,7 @@ export const chaosToolset = {
4535
4588
  { name: "is_system", description: descRiskRuleIsSystem, type: "boolean" },
4536
4589
  { name: "custom_only", description: descRiskRuleCustomOnly, type: "boolean" },
4537
4590
  { name: "data_source", description: descRiskRuleDataSource },
4538
- { name: "include_all_scope", description: descRiskIncludeAllScope, type: "boolean" },
4591
+ { name: "include_in_passive_scan", description: descRiskRuleIncludeInPassiveScan, type: "boolean" },
4539
4592
  ],
4540
4593
  relatedResources: [
4541
4594
  { resourceType: "scanned_risk", relationship: "flags", description: "Risk rules flag scanned risks during a scan. Use scanned_risk with risk_rule_id=<this rule's identity> to find findings produced by this rule." },
@@ -4555,7 +4608,7 @@ export const chaosToolset = {
4555
4608
  is_system: "isSystem",
4556
4609
  custom_only: "customOnly",
4557
4610
  data_source: "dataSource",
4558
- include_all_scope: "includeAllScope",
4611
+ include_in_passive_scan: "includeInPassiveScan",
4559
4612
  },
4560
4613
  defaultQueryParams: { page: "0", limit: "15" },
4561
4614
  responseExtractor: chaosPageExtract,
@@ -4576,12 +4629,16 @@ export const chaosToolset = {
4576
4629
  bodyBuilder: (input) => {
4577
4630
  const b = coerceBody(input);
4578
4631
  if (!b.identity) {
4579
- throw new Error("Missing required field: identity. Use code-<failure-mode>, e.g. code-outbound-call-without-timeout.");
4632
+ throw new Error("Missing required field: identity. Use a lowercase slug, e.g. retry-without-backoff.");
4580
4633
  }
4581
4634
  if (typeof b.identity !== "string" || !RISK_RULE_IDENTITY_RE.test(b.identity)) {
4582
- throw new Error(`identity must match ${RISK_RULE_IDENTITY_RE.source} (lowercase letters, digits and hyphens), e.g. code-outbound-call-without-timeout.`);
4635
+ throw new Error(`identity must match ${RISK_RULE_IDENTITY_RE.source} (1-64 lowercase letters, digits and hyphens, starting and ending with a letter or digit, no required prefix), e.g. retry-without-backoff.`);
4583
4636
  }
4584
- return { identity: b.identity, ...buildRiskRuleBody(b) };
4637
+ return {
4638
+ identity: b.identity,
4639
+ ...buildRiskRuleBody(b, { defaultDataSource: true }),
4640
+ ...(b.confirmNewRule === true ? { confirmNewRule: true } : {}),
4641
+ };
4585
4642
  },
4586
4643
  responseExtractor: passthrough,
4587
4644
  description: descCreateRiskRule,
@@ -4589,6 +4646,7 @@ export const chaosToolset = {
4589
4646
  description: descBodyRiskRuleCreate,
4590
4647
  fields: [
4591
4648
  { name: "identity", type: "string", required: true, description: descRiskRuleIdentity },
4649
+ { name: "confirmNewRule", type: "boolean", required: false, description: "Set true only after the user has seen a 409 matches list and still wants a new rule." },
4592
4650
  ...RISK_RULE_BODY_FIELDS,
4593
4651
  ],
4594
4652
  },