@taiga-ai/mcp-server 0.36.0-rc.778 → 0.36.0-rc.892
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1181 -66
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -505,6 +505,7 @@ function hide(inst, key, value) {
|
|
|
505
505
|
return own$5(inst, key, value, false);
|
|
506
506
|
}
|
|
507
507
|
/** Adds members a table derives from the instance: each builds on first read and shadows as own data, and assignment shadows the same way, as when these were own properties. */
|
|
508
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
508
509
|
function derived(computes, table) {
|
|
509
510
|
for (const key in computes) {
|
|
510
511
|
const compute = computes[key];
|
|
@@ -658,6 +659,7 @@ function newError(Definition) {
|
|
|
658
659
|
}
|
|
659
660
|
return new Definition();
|
|
660
661
|
}
|
|
662
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
661
663
|
function $constructor(name, initializer, proto, params) {
|
|
662
664
|
const zodProto = {};
|
|
663
665
|
function Internals(def) {
|
|
@@ -788,8 +790,7 @@ const initializer$1 = (inst, def) => {
|
|
|
788
790
|
});
|
|
789
791
|
}
|
|
790
792
|
};
|
|
791
|
-
const $ZodError = $constructor("$ZodError", initializer$1);
|
|
792
|
-
$constructor("$ZodError", initializer$1, void 0, { Parent: Error });
|
|
793
|
+
const $ZodError = /* @__PURE__ */ $constructor("$ZodError", initializer$1);
|
|
793
794
|
/** Get-or-create `obj[key]` as an own data property. A path segment naming an inherited member
|
|
794
795
|
* ("toString", "constructor") would otherwise read through to the prototype, and assigning
|
|
795
796
|
* "__proto__" would hit the setter instead of creating a key. */
|
|
@@ -1078,7 +1079,7 @@ const guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9
|
|
|
1078
1079
|
/** Returns a regex for validating an RFC 9562/4122 UUID.
|
|
1079
1080
|
*
|
|
1080
1081
|
* @param version Optionally specify a version 1-8. If no version is specified, all versions are supported. */
|
|
1081
|
-
const uuid = (version) => {
|
|
1082
|
+
const uuid$1 = (version) => {
|
|
1082
1083
|
if (!version) return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;
|
|
1083
1084
|
return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
|
|
1084
1085
|
};
|
|
@@ -1618,8 +1619,8 @@ const $ZodUUID = /*@__PURE__*/ $constructor("$ZodUUID", (inst, def) => {
|
|
|
1618
1619
|
v8: 8
|
|
1619
1620
|
}[def.version];
|
|
1620
1621
|
if (v === void 0) throw new Error(`Invalid UUID version: "${def.version}"`);
|
|
1621
|
-
def.pattern ?? (def.pattern = uuid(v));
|
|
1622
|
-
} else def.pattern ?? (def.pattern = uuid());
|
|
1622
|
+
def.pattern ?? (def.pattern = uuid$1(v));
|
|
1623
|
+
} else def.pattern ?? (def.pattern = uuid$1());
|
|
1623
1624
|
$ZodStringFormat.init(inst, def);
|
|
1624
1625
|
});
|
|
1625
1626
|
const $ZodEmail = /*@__PURE__*/ $constructor("$ZodEmail", (inst, def) => {
|
|
@@ -5675,6 +5676,14 @@ function object(shape, params) {
|
|
|
5675
5676
|
};
|
|
5676
5677
|
return new ZodObject(def);
|
|
5677
5678
|
}
|
|
5679
|
+
function strictObject(shape, params) {
|
|
5680
|
+
return new ZodObject({
|
|
5681
|
+
type: "object",
|
|
5682
|
+
shape,
|
|
5683
|
+
catchall: never(),
|
|
5684
|
+
...normalizeParams(params)
|
|
5685
|
+
});
|
|
5686
|
+
}
|
|
5678
5687
|
function looseObject(shape, params) {
|
|
5679
5688
|
return new ZodObject({
|
|
5680
5689
|
type: "object",
|
|
@@ -7941,7 +7950,23 @@ var SdkError = class extends Error {
|
|
|
7941
7950
|
stampErrorBrands(this, new.target);
|
|
7942
7951
|
}
|
|
7943
7952
|
};
|
|
7944
|
-
|
|
7953
|
+
/**
|
|
7954
|
+
* An {@linkcode SdkError} subclass for HTTP transport failures.
|
|
7955
|
+
*
|
|
7956
|
+
* Thrown by the streamable HTTP transport when the server responds with a
|
|
7957
|
+
* non-OK status code. Narrows {@linkcode SdkError.data | data} to
|
|
7958
|
+
* {@linkcode SdkHttpErrorData} so consumers can inspect the HTTP status
|
|
7959
|
+
* without unsafe casting.
|
|
7960
|
+
*
|
|
7961
|
+
* @example
|
|
7962
|
+
* ```ts source="./sdkErrors.examples.ts#SdkHttpError_basicUsage"
|
|
7963
|
+
* if (error instanceof SdkHttpError) {
|
|
7964
|
+
* console.log(error.status); // number
|
|
7965
|
+
* console.log(error.statusText); // string | undefined
|
|
7966
|
+
* }
|
|
7967
|
+
* ```
|
|
7968
|
+
*/
|
|
7969
|
+
var SdkHttpError = class extends SdkError {
|
|
7945
7970
|
static {
|
|
7946
7971
|
Object.defineProperty(this, "mcpBrand", { value: "mcp.SdkHttpError" });
|
|
7947
7972
|
}
|
|
@@ -7958,7 +7983,7 @@ var SdkError = class extends Error {
|
|
|
7958
7983
|
get statusText() {
|
|
7959
7984
|
return this.data.statusText;
|
|
7960
7985
|
}
|
|
7961
|
-
}
|
|
7986
|
+
};
|
|
7962
7987
|
function isPlainObject$7(value) {
|
|
7963
7988
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7964
7989
|
}
|
|
@@ -23874,6 +23899,18 @@ var TaigaClient = class {
|
|
|
23874
23899
|
async getLicenseSummary(productId) {
|
|
23875
23900
|
return this.request("GET", `/products/${seg(productId)}/maintenance/licenses`);
|
|
23876
23901
|
}
|
|
23902
|
+
/** One licence across a product's inventory (PRO-3251): what it asks, when to allow it, what brings it in. */
|
|
23903
|
+
async getProductLicense(productId, license) {
|
|
23904
|
+
return this.request("GET", `/products/${seg(productId)}/maintenance/licenses/detail?license=${encodeURIComponent(license)}`);
|
|
23905
|
+
}
|
|
23906
|
+
/** Allow a licence in one product: every licence finding in force for it there ignored (PRO-3727). */
|
|
23907
|
+
async allowProductLicense(productId, body) {
|
|
23908
|
+
return this.request("POST", `/products/${seg(productId)}/maintenance/licenses/ignore`, body);
|
|
23909
|
+
}
|
|
23910
|
+
/** Allow a licence in one product no longer: every one of its ignored licence findings there open again. */
|
|
23911
|
+
async reopenProductLicense(productId, license) {
|
|
23912
|
+
return this.request("POST", `/products/${seg(productId)}/maintenance/licenses/reopen`, { license });
|
|
23913
|
+
}
|
|
23877
23914
|
/** The organization's approved list and every licence in use across its products (PRO-3311). */
|
|
23878
23915
|
async getOrganizationLicenses() {
|
|
23879
23916
|
return this.request("GET", "/tenants/current/licenses");
|
|
@@ -23901,6 +23938,10 @@ var TaigaClient = class {
|
|
|
23901
23938
|
async updateFinding(id, body) {
|
|
23902
23939
|
return this.request("PATCH", `/maintenance/findings/${seg(id)}`, body);
|
|
23903
23940
|
}
|
|
23941
|
+
/** Ship notices (PRO-4165): raise the initiative that builds the product's open-source notices, or get the open one. */
|
|
23942
|
+
async shipNotices(productId) {
|
|
23943
|
+
return this.request("POST", `/products/${seg(productId)}/maintenance/notices`, {});
|
|
23944
|
+
}
|
|
23904
23945
|
async remediateFinding(productId, findingId) {
|
|
23905
23946
|
return this.request("POST", `/products/${seg(productId)}/maintenance/findings/${seg(findingId)}/remediate`, {});
|
|
23906
23947
|
}
|
|
@@ -24040,7 +24081,7 @@ var TaigaClient = class {
|
|
|
24040
24081
|
async refreshRealityDocuments(productId, docTypes) {
|
|
24041
24082
|
return this.request("POST", `/products/${seg(productId)}/documents/refresh-reality`, docTypes ? { docTypes } : void 0);
|
|
24042
24083
|
}
|
|
24043
|
-
/** What still blocks finishing setup: required documents unpublished
|
|
24084
|
+
/** What still blocks finishing setup: the required documents unpublished (PRO-2800). */
|
|
24044
24085
|
async getProductSetupBlockers(productId) {
|
|
24045
24086
|
return this.request("GET", `/products/${seg(productId)}/setup`);
|
|
24046
24087
|
}
|
|
@@ -24089,6 +24130,25 @@ var TaigaClient = class {
|
|
|
24089
24130
|
}
|
|
24090
24131
|
};
|
|
24091
24132
|
//#endregion
|
|
24133
|
+
//#region ../../packages/types/dist/clouds.js
|
|
24134
|
+
/**
|
|
24135
|
+
* Cloud provider platforms that Taiga can connect to.
|
|
24136
|
+
*/
|
|
24137
|
+
const CLOUD_PROVIDERS = [
|
|
24138
|
+
"aws",
|
|
24139
|
+
"gcp",
|
|
24140
|
+
"azure",
|
|
24141
|
+
"upcloud",
|
|
24142
|
+
"kubernetes",
|
|
24143
|
+
"digitalocean",
|
|
24144
|
+
"vercel",
|
|
24145
|
+
"railway",
|
|
24146
|
+
"render",
|
|
24147
|
+
"fly",
|
|
24148
|
+
"on_prem",
|
|
24149
|
+
"other"
|
|
24150
|
+
];
|
|
24151
|
+
//#endregion
|
|
24092
24152
|
//#region ../../packages/types/dist/lenient-schema.js
|
|
24093
24153
|
/**
|
|
24094
24154
|
* Building blocks for reading JSON that arrives untyped -- a stored jsonb
|
|
@@ -24150,6 +24210,14 @@ const optionalUuid = string$2().uuid().optional();
|
|
|
24150
24210
|
*/
|
|
24151
24211
|
const optionalLineAnchor = number$1().int().min(0).optional().transform((v) => v ? v : void 0);
|
|
24152
24212
|
//#endregion
|
|
24213
|
+
//#region ../../packages/types/dist/entitlements.js
|
|
24214
|
+
/**
|
|
24215
|
+
* Which agent implementation serves a planning job or build run (PRO-1331).
|
|
24216
|
+
* Recorded on `agent_jobs.runtime` / `build_runs.runtime` at dispatch so
|
|
24217
|
+
* outcomes can be compared per runtime and the UI can badge runs.
|
|
24218
|
+
*/
|
|
24219
|
+
const AGENT_RUNTIMES = ["legacy", "engineer"];
|
|
24220
|
+
//#endregion
|
|
24153
24221
|
//#region ../../packages/types/dist/tenants.js
|
|
24154
24222
|
const TENANT_DOCUMENT_TYPES = [
|
|
24155
24223
|
"policy_security",
|
|
@@ -24350,21 +24418,58 @@ const EU_COUNTRIES = [
|
|
|
24350
24418
|
}
|
|
24351
24419
|
];
|
|
24352
24420
|
/**
|
|
24353
|
-
* EEA countries outside the EU
|
|
24354
|
-
*
|
|
24421
|
+
* EEA countries outside the EU, all three, so {@link COUNTRIES} covers the
|
|
24422
|
+
* whole EU and EEA (PRO-3791). Only Norway has a country layer; Iceland and
|
|
24423
|
+
* Liechtenstein get the `eea` region layer alone, and neither is offered as a
|
|
24424
|
+
* Taiga tenant country ({@link offeredCountries}).
|
|
24425
|
+
*/
|
|
24426
|
+
const EEA_COUNTRIES = [
|
|
24427
|
+
{
|
|
24428
|
+
code: "NO",
|
|
24429
|
+
name: "Norway"
|
|
24430
|
+
},
|
|
24431
|
+
{
|
|
24432
|
+
code: "IS",
|
|
24433
|
+
name: "Iceland"
|
|
24434
|
+
},
|
|
24435
|
+
{
|
|
24436
|
+
code: "LI",
|
|
24437
|
+
name: "Liechtenstein"
|
|
24438
|
+
}
|
|
24439
|
+
];
|
|
24440
|
+
/**
|
|
24441
|
+
* The United Kingdom (PRO-3420): ISO code `GB`, neither EU nor EEA, in the
|
|
24442
|
+
* `uk` region. Offered only while the `regulations` flag is on.
|
|
24355
24443
|
*/
|
|
24356
|
-
const
|
|
24357
|
-
code: "
|
|
24358
|
-
name: "
|
|
24444
|
+
const UK_COUNTRIES = [{
|
|
24445
|
+
code: "GB",
|
|
24446
|
+
name: "United Kingdom"
|
|
24359
24447
|
}];
|
|
24360
|
-
[
|
|
24448
|
+
[
|
|
24449
|
+
...EU_COUNTRIES,
|
|
24450
|
+
...EEA_COUNTRIES,
|
|
24451
|
+
...UK_COUNTRIES
|
|
24452
|
+
];
|
|
24361
24453
|
/** Countries available during closed beta. Expand as we open new markets. */
|
|
24362
24454
|
const BETA_COUNTRIES = ["FI"];
|
|
24455
|
+
[...EU_COUNTRIES.map((c) => c.code).filter((code) => !BETA_COUNTRIES.includes(code))];
|
|
24363
24456
|
EU_COUNTRIES.filter((c) => BETA_COUNTRIES.includes(c.code));
|
|
24364
|
-
Object.fromEntries([
|
|
24457
|
+
Object.fromEntries([
|
|
24458
|
+
...EU_COUNTRIES.map((c) => [c.code, "eu"]),
|
|
24459
|
+
...EEA_COUNTRIES.map((c) => [c.code, "eea"]),
|
|
24460
|
+
...UK_COUNTRIES.map((c) => [c.code, "uk"])
|
|
24461
|
+
]);
|
|
24365
24462
|
//#endregion
|
|
24366
24463
|
//#region ../../packages/types/dist/factories.js
|
|
24367
24464
|
const FACTORY_DOCUMENT_TYPES = ["factory_instructions"];
|
|
24465
|
+
/** The single canonical factory-document type (the free-form Factory Instructions doc). */
|
|
24466
|
+
const FACTORY_INSTRUCTIONS_TYPE = "factory_instructions";
|
|
24467
|
+
/**
|
|
24468
|
+
* The pre-PRO-1671 value of that type. Old clients still name it in URLs
|
|
24469
|
+
* (`/factories/:id/documents/team_instructions`), so the API accepts it as
|
|
24470
|
+
* an alias for a deprecation window.
|
|
24471
|
+
*/
|
|
24472
|
+
const LEGACY_FACTORY_INSTRUCTIONS_TYPE = "team_instructions";
|
|
24368
24473
|
//#endregion
|
|
24369
24474
|
//#region ../../packages/types/dist/forge.js
|
|
24370
24475
|
/** Source-control hosts. `bitbucket` is a stored link provider without an adapter yet. */
|
|
@@ -24416,6 +24521,15 @@ const FORGE_SUPPORT = {
|
|
|
24416
24521
|
],
|
|
24417
24522
|
bitbucket: []
|
|
24418
24523
|
};
|
|
24524
|
+
/**
|
|
24525
|
+
* Whether a repository link under this forge may name a GitHub App
|
|
24526
|
+
* installation's repository: only GitHub's. Stored under another forge, the
|
|
24527
|
+
* link would be dispatched with GitHub credentials while the rest of its
|
|
24528
|
+
* lifecycle reads it as that forge.
|
|
24529
|
+
*/
|
|
24530
|
+
function linksGitHubAppRepositories(provider) {
|
|
24531
|
+
return provider === "github";
|
|
24532
|
+
}
|
|
24419
24533
|
const FORGE_TERMS = {
|
|
24420
24534
|
github: {
|
|
24421
24535
|
name: "GitHub",
|
|
@@ -24471,6 +24585,11 @@ const PRODUCT_DOCUMENT_TYPES = [
|
|
|
24471
24585
|
"product_instructions"
|
|
24472
24586
|
];
|
|
24473
24587
|
/**
|
|
24588
|
+
* The pre-PRO-1671 value of `product_instructions`. Old clients still name it
|
|
24589
|
+
* in URLs, so the API accepts it as an alias for a deprecation window.
|
|
24590
|
+
*/
|
|
24591
|
+
const LEGACY_PRODUCT_INSTRUCTIONS_TYPE = "project_instructions";
|
|
24592
|
+
/**
|
|
24474
24593
|
* The document types the codebase-intelligence agent can derive from a repo --
|
|
24475
24594
|
* the valid values for a single-document reality refresh (PRO-1141).
|
|
24476
24595
|
*
|
|
@@ -24566,6 +24685,8 @@ const PRODUCT_DOCUMENT_DISPLAY_NAMES = {
|
|
|
24566
24685
|
service_blueprint: "Service Blueprint"
|
|
24567
24686
|
};
|
|
24568
24687
|
const PRODUCT_DOCUMENT_ORIGINS = ["plan", "reality"];
|
|
24688
|
+
/** The forges a repository link can name: `FORGE_PROVIDERS`, under the name links have always used. */
|
|
24689
|
+
const VCS_PROVIDERS = FORGE_PROVIDERS;
|
|
24569
24690
|
//#endregion
|
|
24570
24691
|
//#region ../../packages/types/dist/route-key.js
|
|
24571
24692
|
/**
|
|
@@ -24808,7 +24929,7 @@ const HALT_CAUSES = {
|
|
|
24808
24929
|
* reaped on a dead heartbeat, or found at 'planning' with no live job by the
|
|
24809
24930
|
* reconciler, which fails it with that cause on the tick that notices
|
|
24810
24931
|
* (`armPlanning` writes the status and creates the job in ONE transaction,
|
|
24811
|
-
* so that shape is never a race). One of
|
|
24932
|
+
* so that shape is never a race). One of four halts that mirror
|
|
24812
24933
|
* `PLAN_FAILURE_CAUSES` one-to-one; see `PLAN_FAILURE_HALT`.
|
|
24813
24934
|
*/
|
|
24814
24935
|
planning_stalled: {
|
|
@@ -24821,6 +24942,22 @@ const HALT_CAUSES = {
|
|
|
24821
24942
|
"remove"
|
|
24822
24943
|
]
|
|
24823
24944
|
},
|
|
24945
|
+
/**
|
|
24946
|
+
* The planner concluded nothing should be built (PRO-3941): a licence's
|
|
24947
|
+
* replacement where only build tools carry the licence and no maintained
|
|
24948
|
+
* package replaces them. Not a fault, so not `planning_failed`; its reason
|
|
24949
|
+
* is the detail, read off the planning job as a failure's is.
|
|
24950
|
+
*/
|
|
24951
|
+
planning_declined: {
|
|
24952
|
+
message: "The plan recommends not building this, so the queue stopped here.",
|
|
24953
|
+
params: [],
|
|
24954
|
+
clearedWhen: "planning is retried, or a re-plan replaces it",
|
|
24955
|
+
exits: [
|
|
24956
|
+
"auto",
|
|
24957
|
+
"reorder",
|
|
24958
|
+
"remove"
|
|
24959
|
+
]
|
|
24960
|
+
},
|
|
24824
24961
|
/** The run errored out. */
|
|
24825
24962
|
run_failed: {
|
|
24826
24963
|
message: "The run failed, so the queue stopped here.",
|
|
@@ -25102,7 +25239,8 @@ const READY_BLOCKERS = [
|
|
|
25102
25239
|
"bar_lowering",
|
|
25103
25240
|
"bar_lowering_unchecked",
|
|
25104
25241
|
"criticals_unresolved",
|
|
25105
|
-
"clean_checkout_failed"
|
|
25242
|
+
"clean_checkout_failed",
|
|
25243
|
+
"license_not_approved"
|
|
25106
25244
|
];
|
|
25107
25245
|
/**
|
|
25108
25246
|
* Why the autonomous loop handed a live pull request to a person (PRO-2409).
|
|
@@ -25215,6 +25353,7 @@ const INBOX_ITEM_KINDS = [
|
|
|
25215
25353
|
"review_pr",
|
|
25216
25354
|
"review_maintenance",
|
|
25217
25355
|
"review_policy",
|
|
25356
|
+
"attest_policy",
|
|
25218
25357
|
"setup_env_var",
|
|
25219
25358
|
"setup_secret",
|
|
25220
25359
|
"setup_third_party",
|
|
@@ -25524,6 +25663,29 @@ object({
|
|
|
25524
25663
|
[...BUILD_RUN_ACTIVE_STATUSES.filter((s) => s !== "reviewing")];
|
|
25525
25664
|
//#endregion
|
|
25526
25665
|
//#region ../../packages/types/dist/agents.js
|
|
25666
|
+
const AGENT_JOB_TYPES = [
|
|
25667
|
+
"generate_document",
|
|
25668
|
+
"generate_backlog",
|
|
25669
|
+
"plan_backlog_item",
|
|
25670
|
+
"infer_backlog_dependencies",
|
|
25671
|
+
"deploy",
|
|
25672
|
+
"provision_infrastructure",
|
|
25673
|
+
"health_check",
|
|
25674
|
+
"refresh_reality_docs",
|
|
25675
|
+
"derive_policy_update",
|
|
25676
|
+
"derive_design_from_documents",
|
|
25677
|
+
"sync_design_tokens",
|
|
25678
|
+
"generate_design_component",
|
|
25679
|
+
"maintenance_sweep",
|
|
25680
|
+
"maintenance_verify",
|
|
25681
|
+
"propose_ui_concepts",
|
|
25682
|
+
"iterate_ui_concept",
|
|
25683
|
+
"fill_questionnaire",
|
|
25684
|
+
"read_questionnaire_layout",
|
|
25685
|
+
"generate_ui_direction",
|
|
25686
|
+
"assess_policy_conformance",
|
|
25687
|
+
"replan_run"
|
|
25688
|
+
];
|
|
25527
25689
|
/**
|
|
25528
25690
|
* Why a PROMPTED `generate_backlog` job planned nothing.
|
|
25529
25691
|
*
|
|
@@ -25932,10 +26094,34 @@ const MCP_TOOLSETS = [
|
|
|
25932
26094
|
"observe",
|
|
25933
26095
|
"documents"
|
|
25934
26096
|
];
|
|
26097
|
+
Object.freeze({
|
|
26098
|
+
required: false,
|
|
26099
|
+
gracePeriodDays: 0,
|
|
26100
|
+
requiredSince: null
|
|
26101
|
+
});
|
|
25935
26102
|
[...TURN_EVENT_TRIGGERS];
|
|
25936
26103
|
/** Longest request for changes from Taiga (PRO-3035): about four pages of prose. */
|
|
25937
26104
|
const CHANGE_REQUEST_MAX_CHARS = 1e4;
|
|
25938
26105
|
//#endregion
|
|
26106
|
+
//#region ../../packages/types/dist/requests.js
|
|
26107
|
+
/** Every way a request can be answered, whatever its kind. */
|
|
26108
|
+
const REQUEST_EXITS = [
|
|
26109
|
+
"resume_run",
|
|
26110
|
+
"replan",
|
|
26111
|
+
"clear",
|
|
26112
|
+
"leave_line",
|
|
26113
|
+
"approve",
|
|
26114
|
+
"reject",
|
|
26115
|
+
"merge",
|
|
26116
|
+
"close",
|
|
26117
|
+
"request_changes",
|
|
26118
|
+
"done",
|
|
26119
|
+
"dismiss",
|
|
26120
|
+
"reviewed"
|
|
26121
|
+
];
|
|
26122
|
+
/** The most ids an answer's `seen` carries; a longer list is not sent, and the review is not checked. */
|
|
26123
|
+
const REVIEW_SEEN_MAX = 1e4;
|
|
26124
|
+
//#endregion
|
|
25939
26125
|
//#region ../../packages/types/dist/maintenance.js
|
|
25940
26126
|
/**
|
|
25941
26127
|
* Maintenance taxonomy + API DTOs.
|
|
@@ -26030,9 +26216,15 @@ function isFixableFinding(finding) {
|
|
|
26030
26216
|
if (finding.status !== "open") return false;
|
|
26031
26217
|
if (finding.location?.historyOnly) return false;
|
|
26032
26218
|
if (!isAgentRemediable(finding)) return false;
|
|
26219
|
+
if (finding.fingerprint?.startsWith("base-image:")) return typeof finding.baseMove === "boolean" ? finding.baseMove : baseImageMoveRemoves(finding.data);
|
|
26033
26220
|
if (finding.kind === "vulnerable_dependency" && isMaliciousPackageFinding(finding)) return true;
|
|
26034
26221
|
return isCodeRemediationKind(finding.kind) || !!finding.recommendedVersion;
|
|
26035
26222
|
}
|
|
26223
|
+
function baseImageMoveRemoves(data) {
|
|
26224
|
+
const baseImage = typeof data === "object" && data !== null ? data.baseImage : null;
|
|
26225
|
+
const bases = typeof baseImage === "object" && baseImage !== null ? baseImage.bases : null;
|
|
26226
|
+
return Array.isArray(bases) && bases.length > 0 && bases.every((b) => typeof b === "object" && b !== null && typeof b.movesTo === "string" && b.removed === true);
|
|
26227
|
+
}
|
|
26036
26228
|
/**
|
|
26037
26229
|
* A malicious package (OSV `MAL-*`): its advisory id or one of its aliases
|
|
26038
26230
|
* says so, or its tier is Act now with no KEV listing, which only a malware
|
|
@@ -26049,6 +26241,17 @@ function isMaliciousPackageFinding(finding) {
|
|
|
26049
26241
|
if (Array.isArray(aliases) && aliases.some(mal)) return true;
|
|
26050
26242
|
return finding.tier === "act_now" && finding.kevListedAt == null;
|
|
26051
26243
|
}
|
|
26244
|
+
/**
|
|
26245
|
+
* The scanner CATEGORIES a sweep reports, in the order the agent reports them
|
|
26246
|
+
* (PRO-1725). Outcomes are keyed on the category, not the tool, so replacing a
|
|
26247
|
+
* tool in the agents' scanner manifest changes nothing here.
|
|
26248
|
+
*/
|
|
26249
|
+
const MAINTENANCE_SCANNERS = [
|
|
26250
|
+
"deps",
|
|
26251
|
+
"secrets",
|
|
26252
|
+
"sast",
|
|
26253
|
+
"iac"
|
|
26254
|
+
];
|
|
26052
26255
|
new Map(Object.entries({
|
|
26053
26256
|
deps: [
|
|
26054
26257
|
"vulnerable_dependency",
|
|
@@ -26254,6 +26457,19 @@ object({
|
|
|
26254
26457
|
commit: d.commit
|
|
26255
26458
|
}));
|
|
26256
26459
|
/**
|
|
26460
|
+
* The sources whose findings no agent scanner's outcome decides (PRO-4070):
|
|
26461
|
+
* the API's runtime judge (PRO-3659) and base OS judge (PRO-4074), and the
|
|
26462
|
+
* base image reading (PRO-1728), whose findings the agent drafts but only a
|
|
26463
|
+
* sweep that read the images may settle.
|
|
26464
|
+
*/
|
|
26465
|
+
const MAINTENANCE_JUDGED_SOURCES = [
|
|
26466
|
+
"runtime",
|
|
26467
|
+
"base_os",
|
|
26468
|
+
"base_image"
|
|
26469
|
+
];
|
|
26470
|
+
[...MAINTENANCE_SCANNERS, ...MAINTENANCE_JUDGED_SOURCES];
|
|
26471
|
+
new Set(MAINTENANCE_SCANNERS);
|
|
26472
|
+
/**
|
|
26257
26473
|
* The longest licence string the package list filters by (PRO-2920). The
|
|
26258
26474
|
* inventory keeps every expression whole whatever its length; this bounds only
|
|
26259
26475
|
* the query string, far above any expression seen. A longer one is listed in
|
|
@@ -26261,6 +26477,213 @@ object({
|
|
|
26261
26477
|
*/
|
|
26262
26478
|
const MAINTENANCE_LICENSE_FILTER_MAX = 4096;
|
|
26263
26479
|
//#endregion
|
|
26480
|
+
//#region ../../packages/types/dist/maintenance-advisory.js
|
|
26481
|
+
/**
|
|
26482
|
+
* Pull the renderable advisory fields out of a finding's raw OSV `data` object.
|
|
26483
|
+
*
|
|
26484
|
+
* `data` is the scanner's raw OSV vulnerability payload (MaintenanceFindingData,
|
|
26485
|
+
* shape varies), so every field is read leniently: a missing or malformed key
|
|
26486
|
+
* yields an empty value rather than throwing. Shared by the web app's finding
|
|
26487
|
+
* detail and the Taiga Audit preview and report (PRO-3791), so both show the
|
|
26488
|
+
* same identifiers for the same finding.
|
|
26489
|
+
*/
|
|
26490
|
+
/** A non-empty string, or null: anything else the scanner wrote reads as absent. */
|
|
26491
|
+
const text$3 = string$2().min(1).nullable().catch(null);
|
|
26492
|
+
/** The non-empty strings of a list; a value that is not a list reads as none. */
|
|
26493
|
+
const texts = array(unknown$1()).catch([]).transform((list) => list.filter((v) => typeof v === "string" && v.length > 0));
|
|
26494
|
+
object({
|
|
26495
|
+
type: text$3,
|
|
26496
|
+
score: text$3
|
|
26497
|
+
});
|
|
26498
|
+
object({
|
|
26499
|
+
type: text$3,
|
|
26500
|
+
url: text$3
|
|
26501
|
+
});
|
|
26502
|
+
object({
|
|
26503
|
+
aliases: texts,
|
|
26504
|
+
database_specific: object({
|
|
26505
|
+
cwe_ids: texts,
|
|
26506
|
+
severity: text$3
|
|
26507
|
+
}).nullable().catch(null),
|
|
26508
|
+
severity: array(unknown$1()).catch([]),
|
|
26509
|
+
references: array(unknown$1()).catch([]),
|
|
26510
|
+
published: text$3,
|
|
26511
|
+
modified: text$3
|
|
26512
|
+
});
|
|
26513
|
+
//#endregion
|
|
26514
|
+
//#region ../../packages/types/dist/finding-controls.js
|
|
26515
|
+
/**
|
|
26516
|
+
* A committed credential.
|
|
26517
|
+
*
|
|
26518
|
+
* SEC-3.4 "Secrets must never be stored in source code or logs"
|
|
26519
|
+
* SEC-5.1 "Secrets must be stored in an approved secrets management system"
|
|
26520
|
+
* SEC-5.2 "Secrets must be rotated at least annually" -- the half Taiga cannot
|
|
26521
|
+
* do, and the reason the remediation initiative insists the PR say so.
|
|
26522
|
+
* SDLC-3.5 "Secret detection must run pre-commit and in CI pipeline"
|
|
26523
|
+
*/
|
|
26524
|
+
const SECRET_CONTROLS = [
|
|
26525
|
+
"SEC-3.4",
|
|
26526
|
+
"SEC-5.1",
|
|
26527
|
+
"SEC-5.2",
|
|
26528
|
+
"SDLC-3.5"
|
|
26529
|
+
];
|
|
26530
|
+
/**
|
|
26531
|
+
* A vulnerable dependency.
|
|
26532
|
+
*
|
|
26533
|
+
* SCS-2.6 "critical security patches within 7 days, others within 30 days"
|
|
26534
|
+
* -- breached by a vulnerability whose fix has been out past its
|
|
26535
|
+
* window, and by no other (PRO-3420). One found this morning has missed
|
|
26536
|
+
* nothing; neither has one with no fixed release to move to.
|
|
26537
|
+
* SCS-5.2 "Critical vulnerabilities must block deployment"
|
|
26538
|
+
*
|
|
26539
|
+
* Two controls this map deliberately does NOT claim:
|
|
26540
|
+
* - SCS-2.5 "Transitive dependency vulnerabilities must be monitored
|
|
26541
|
+
* continuously". A finding is that monitoring working, not evidence it is
|
|
26542
|
+
* missing; every vulnerable dependency read as a breach of it. The sweep's
|
|
26543
|
+
* own record shows it met instead (sweep-evidence.ts).
|
|
26544
|
+
* - Anything for an outdated dependency that is no vulnerability: SCS-2.6's
|
|
26545
|
+
* rationale is "timely vulnerability remediation", and an update that fixes
|
|
26546
|
+
* nothing is not a security patch.
|
|
26547
|
+
*/
|
|
26548
|
+
const DEPENDENCY_CONTROLS = ["SCS-2.6", "SCS-5.2"];
|
|
26549
|
+
/**
|
|
26550
|
+
* A package whose licence the allowlist does not satisfy (PRO-2862).
|
|
26551
|
+
*
|
|
26552
|
+
* TIC-6.2 "Open source licenses must be reviewed and approved before adoption"
|
|
26553
|
+
* -- the same policy section lists the approved licences and the ones
|
|
26554
|
+
* requiring review, which is where the default allowlist comes from.
|
|
26555
|
+
*/
|
|
26556
|
+
const LICENSE_CONTROLS = ["TIC-6.2"];
|
|
26557
|
+
/**
|
|
26558
|
+
* Injection and unsanitized input, whatever the sink.
|
|
26559
|
+
*
|
|
26560
|
+
* SDLC-5.1 "All user input must be validated and sanitized"
|
|
26561
|
+
* SEC-6.1 "All API endpoints must validate and sanitize input"
|
|
26562
|
+
*/
|
|
26563
|
+
const INJECTION_CONTROLS = ["SDLC-5.1", "SEC-6.1"];
|
|
26564
|
+
/**
|
|
26565
|
+
* Every opengrep rule Taiga ships, mapped explicitly.
|
|
26566
|
+
*
|
|
26567
|
+
* A closed set, unlike checkov's -- these rules are ours, so an exhaustive map
|
|
26568
|
+
* is possible and a keyword heuristic would be a worse answer to an easier
|
|
26569
|
+
* question. `policy-map.test.ts` reads the rule ids straight out of the YAML
|
|
26570
|
+
* so a new rule that lands unmapped fails the build instead of silently
|
|
26571
|
+
* emitting no control.
|
|
26572
|
+
*/
|
|
26573
|
+
const SAST_CONTROLS = {
|
|
26574
|
+
"taiga.csharp.sql-string-concat": INJECTION_CONTROLS,
|
|
26575
|
+
"taiga.go.sql-string-concat": INJECTION_CONTROLS,
|
|
26576
|
+
"taiga.javascript.sql-template-concat": INJECTION_CONTROLS,
|
|
26577
|
+
"taiga.python.sqli-fstring": INJECTION_CONTROLS,
|
|
26578
|
+
"taiga.rust.sql-format-concat": INJECTION_CONTROLS,
|
|
26579
|
+
"taiga.csharp.process-start-shell-dynamic": INJECTION_CONTROLS,
|
|
26580
|
+
"taiga.go.exec-command-dynamic": INJECTION_CONTROLS,
|
|
26581
|
+
"taiga.javascript.child-process-exec-dynamic": INJECTION_CONTROLS,
|
|
26582
|
+
"taiga.python.os-system-dynamic": INJECTION_CONTROLS,
|
|
26583
|
+
"taiga.python.subprocess-shell-true": INJECTION_CONTROLS,
|
|
26584
|
+
"taiga.rust.command-shell-dynamic": INJECTION_CONTROLS,
|
|
26585
|
+
"taiga.javascript.eval-dynamic": INJECTION_CONTROLS,
|
|
26586
|
+
"taiga.python.eval-exec-dynamic": INJECTION_CONTROLS,
|
|
26587
|
+
"taiga.javascript.dangerously-set-inner-html": INJECTION_CONTROLS,
|
|
26588
|
+
"taiga.javascript.innerhtml-dynamic": INJECTION_CONTROLS,
|
|
26589
|
+
"taiga.csharp.binaryformatter-deserialize": ["SDLC-5.7"],
|
|
26590
|
+
"taiga.python.pickle-load-untrusted": ["SDLC-5.7"],
|
|
26591
|
+
"taiga.python.yaml-load-unsafe": ["SDLC-5.7"],
|
|
26592
|
+
"taiga.csharp.cert-validation-disabled": ["SEC-3.2"],
|
|
26593
|
+
"taiga.go.tls-insecure-skip-verify": ["SEC-3.2"],
|
|
26594
|
+
"taiga.javascript.tls-reject-unauthorized-false": ["SEC-3.2"],
|
|
26595
|
+
"taiga.python.requests-verify-false": ["SEC-3.2"],
|
|
26596
|
+
"taiga.rust.tls-verification-disabled": ["SEC-3.2"],
|
|
26597
|
+
"taiga.javascript.jwt-verify-none": ["SDLC-5.4", "SEC-9.1"],
|
|
26598
|
+
"taiga.python.flask-debug-true": ["SDLC-5.5"],
|
|
26599
|
+
"taiga.go.pprof-import": ["SDLC-5.5"],
|
|
26600
|
+
"taiga.csharp.weak-hash": [],
|
|
26601
|
+
"taiga.go.weak-hash": [],
|
|
26602
|
+
"taiga.javascript.weak-hash": [],
|
|
26603
|
+
"taiga.python.weak-hash": [],
|
|
26604
|
+
"taiga.rust.weak-hash": [],
|
|
26605
|
+
"taiga.go.math-rand-for-secrets": [],
|
|
26606
|
+
"taiga.rust.non-crypto-rng-for-secrets": []
|
|
26607
|
+
};
|
|
26608
|
+
/**
|
|
26609
|
+
* Checkov, matched on the check's own description.
|
|
26610
|
+
*
|
|
26611
|
+
* Checkov's rule set is open and numbers in the thousands, so an exhaustive
|
|
26612
|
+
* id map is not available the way it is for our own rules. These phrases are
|
|
26613
|
+
* matched against the check title checkov itself writes ("Ensure all data
|
|
26614
|
+
* stored in the RDS is securely encrypted at rest"), and they are deliberately
|
|
26615
|
+
* narrow: a title matching nothing yields no control, which is the safe
|
|
26616
|
+
* direction to be wrong in.
|
|
26617
|
+
*
|
|
26618
|
+
* Exported so `policy-map.test.ts` validates THESE ids against the registry
|
|
26619
|
+
* too. They are the half most likely to drift, and an id-map that only
|
|
26620
|
+
* validates the closed half is a claim the tests do not support.
|
|
26621
|
+
*/
|
|
26622
|
+
const IAC_PHRASES = [
|
|
26623
|
+
{
|
|
26624
|
+
pattern: /encrypt\w*\s+at\s+rest|at\s+rest.*encrypt|securely encrypted/i,
|
|
26625
|
+
controls: ["SEC-3.1"],
|
|
26626
|
+
why: "at-rest encryption"
|
|
26627
|
+
},
|
|
26628
|
+
{
|
|
26629
|
+
pattern: /\bKMS\b|customer[- ]managed key|\bCMK\b/i,
|
|
26630
|
+
controls: ["SEC-3.3"],
|
|
26631
|
+
why: "key management"
|
|
26632
|
+
},
|
|
26633
|
+
{
|
|
26634
|
+
pattern: /in\s+transit|\bTLS\b|\bSSL\b|\bHTTPS\b/i,
|
|
26635
|
+
controls: ["SEC-3.2"],
|
|
26636
|
+
why: "transport encryption"
|
|
26637
|
+
},
|
|
26638
|
+
{
|
|
26639
|
+
pattern: /0\.0\.0\.0\/0|ingress from|security group/i,
|
|
26640
|
+
controls: ["CLD-7.2"],
|
|
26641
|
+
why: "network default-deny"
|
|
26642
|
+
},
|
|
26643
|
+
{
|
|
26644
|
+
pattern: /public(ly)?\s+(access|read|write|expos)|public access/i,
|
|
26645
|
+
controls: ["CLD-1.3"],
|
|
26646
|
+
why: "public exposure"
|
|
26647
|
+
},
|
|
26648
|
+
{
|
|
26649
|
+
pattern: /least privilege|wildcard|admin(istrator)? (policy|access)/i,
|
|
26650
|
+
controls: ["SEC-2.1"],
|
|
26651
|
+
why: "least privilege"
|
|
26652
|
+
},
|
|
26653
|
+
{
|
|
26654
|
+
pattern: /backup|point[- ]in[- ]time recovery/i,
|
|
26655
|
+
controls: ["OPS-4.1"],
|
|
26656
|
+
why: "backup"
|
|
26657
|
+
},
|
|
26658
|
+
{
|
|
26659
|
+
pattern: /audit log|cloudtrail|access logging|logging (is )?enabled/i,
|
|
26660
|
+
controls: ["SDLC-5.8"],
|
|
26661
|
+
why: "security logging"
|
|
26662
|
+
}
|
|
26663
|
+
];
|
|
26664
|
+
[.../* @__PURE__ */ new Set([
|
|
26665
|
+
...SECRET_CONTROLS,
|
|
26666
|
+
...DEPENDENCY_CONTROLS,
|
|
26667
|
+
...LICENSE_CONTROLS,
|
|
26668
|
+
...Object.values(SAST_CONTROLS).flat(),
|
|
26669
|
+
...IAC_PHRASES.flatMap((p) => p.controls)
|
|
26670
|
+
])];
|
|
26671
|
+
new RegExp(`^\\|\\s*(${CONTROL_ID_SOURCE})\\s*\\|\\s*(.*?)\\s*(?:\\||$)`);
|
|
26672
|
+
REALITY_DERIVED_DOCUMENT_TYPES.filter((t) => t !== "codebase_snapshot");
|
|
26673
|
+
//#endregion
|
|
26674
|
+
//#region ../../packages/types/dist/custom-documents.js
|
|
26675
|
+
/**
|
|
26676
|
+
* Shared types for the custom-documents feature: customer-uploaded reference
|
|
26677
|
+
* files (PDFs, Markdown, OpenAPI specs, etc.) indexed into a Bedrock
|
|
26678
|
+
* Knowledge Base for retrieval by Taiga's internal agents.
|
|
26679
|
+
*/
|
|
26680
|
+
const CUSTOM_DOCUMENT_STATUSES = [
|
|
26681
|
+
"pending",
|
|
26682
|
+
"ingesting",
|
|
26683
|
+
"ready",
|
|
26684
|
+
"failed"
|
|
26685
|
+
];
|
|
26686
|
+
//#endregion
|
|
26264
26687
|
//#region ../../packages/types/dist/design-tokens.js
|
|
26265
26688
|
/**
|
|
26266
26689
|
* The brand grounding contract (PRO-929).
|
|
@@ -26436,6 +26859,509 @@ object({
|
|
|
26436
26859
|
fontSize: lenient(union([string$2(), number$1()]))
|
|
26437
26860
|
});
|
|
26438
26861
|
//#endregion
|
|
26862
|
+
//#region ../../packages/types/dist/questionnaires.js
|
|
26863
|
+
/**
|
|
26864
|
+
* Questionnaire filling (PRO-932).
|
|
26865
|
+
*
|
|
26866
|
+
* A customer uploads their own requirement spreadsheet; Taiga answers what the
|
|
26867
|
+
* project's evidence settles, cites the document version each answer rests on,
|
|
26868
|
+
* and hands back the gaps. The register these types describe is the durable
|
|
26869
|
+
* object -- the workbook is an import format on one side and an export format
|
|
26870
|
+
* on the other.
|
|
26871
|
+
*/
|
|
26872
|
+
/**
|
|
26873
|
+
* Lifecycle of one questionnaire.
|
|
26874
|
+
*
|
|
26875
|
+
* `filling` is deliberately NOT set by the fill workflow (PRO-1187), and that
|
|
26876
|
+
* is a decision rather than an omission. A status set at the start of a
|
|
26877
|
+
* multi-minute run needs something to clear it when the run does not finish --
|
|
26878
|
+
* and if the worker dies mid-fill, nothing would: `agent_jobs` has a reaper for
|
|
26879
|
+
* stale JOBS, but it knows nothing about this table, so the register would read
|
|
26880
|
+
* `filling` forever. A state nothing can leave is worse than one nothing
|
|
26881
|
+
* enters.
|
|
26882
|
+
*
|
|
26883
|
+
* "Is a fill running" is already answerable, and from the row that has a reaper:
|
|
26884
|
+
* an active `fill_questionnaire` job with this questionnaire as its target.
|
|
26885
|
+
* Wire `filling` up when it comes with a path off it.
|
|
26886
|
+
*/
|
|
26887
|
+
/**
|
|
26888
|
+
* `reading` and `needs_columns` are the import itself (PRO-1506). A workbook
|
|
26889
|
+
* is stored and its row exists from the first byte; the column read runs as
|
|
26890
|
+
* a job. `reading` is that job in flight; `needs_columns` is the read done
|
|
26891
|
+
* without confidence, waiting for a person to confirm the map. Only once the
|
|
26892
|
+
* map is confirmed do items exist and the row becomes `draft`.
|
|
26893
|
+
*/
|
|
26894
|
+
/**
|
|
26895
|
+
* A sheet map's bounds, Excel's own: a column index becomes a column letter
|
|
26896
|
+
* in a cell reference the export writes to, so an unbounded one names
|
|
26897
|
+
* whatever it likes.
|
|
26898
|
+
*/
|
|
26899
|
+
const EXCEL_MAX_COLUMN = 16384;
|
|
26900
|
+
const EXCEL_MAX_ROW = 1048576;
|
|
26901
|
+
//#endregion
|
|
26902
|
+
//#region ../../packages/types/dist/product-network.js
|
|
26903
|
+
/**
|
|
26904
|
+
* Where an engineer build's sandbox may connect to (PRO-2793, part of
|
|
26905
|
+
* PRO-1980).
|
|
26906
|
+
*
|
|
26907
|
+
* The engineer pod's credential gateway is the sandbox's only way out. By
|
|
26908
|
+
* default it lets through a standard list: the public package registries,
|
|
26909
|
+
* toolchain downloads, container registries, GitHub for reading public
|
|
26910
|
+
* dependencies, and the tenant's own registries. A product adds to that:
|
|
26911
|
+
*
|
|
26912
|
+
* - `allowlist` (the default, also what `null` means): the standard list plus
|
|
26913
|
+
* the product's own `networkHosts`.
|
|
26914
|
+
* - `unrestricted`: any public host. For a product whose builds need hosts
|
|
26915
|
+
* nobody can list in advance; the gateway still refuses private and
|
|
26916
|
+
* metadata addresses, and still never hands the sandbox a credential.
|
|
26917
|
+
*
|
|
26918
|
+
* A host is one exact name (`api.example.com`). There are no wildcards: a
|
|
26919
|
+
* wildcard over a multi-label public suffix (`*.co.uk`) or a provider's
|
|
26920
|
+
* namespace would admit every tenant under it, and telling those apart needs
|
|
26921
|
+
* the public suffix list. A build that needs many names under one domain is
|
|
26922
|
+
* what `unrestricted` is for.
|
|
26923
|
+
*/
|
|
26924
|
+
const PRODUCT_NETWORK_ACCESS = ["allowlist", "unrestricted"];
|
|
26925
|
+
/**
|
|
26926
|
+
* A host a product may add: lowercase DNS labels, at least two, ending in a
|
|
26927
|
+
* letter label. No wildcard, scheme, port, path or IP literal.
|
|
26928
|
+
*/
|
|
26929
|
+
const PRODUCT_NETWORK_HOST_PATTERN = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z0-9-]{0,61}[a-z0-9]$/;
|
|
26930
|
+
/**
|
|
26931
|
+
* Domains where anyone can host content under their own name: a bucket, an
|
|
26932
|
+
* app, a tunnel. Listing one whole would let a prompt injection send code to
|
|
26933
|
+
* a bucket the attacker made, the way the gateway's standard list never lists
|
|
26934
|
+
* a shared CDN whole. A specific name under one (`my-bucket.s3.amazonaws.com`)
|
|
26935
|
+
* is fine.
|
|
26936
|
+
*/
|
|
26937
|
+
const SHARED_HOSTING_DOMAINS = [
|
|
26938
|
+
"amazonaws.com",
|
|
26939
|
+
"s3.amazonaws.com",
|
|
26940
|
+
"cloudfront.net",
|
|
26941
|
+
"googleapis.com",
|
|
26942
|
+
"storage.googleapis.com",
|
|
26943
|
+
"appspot.com",
|
|
26944
|
+
"firebaseapp.com",
|
|
26945
|
+
"web.app",
|
|
26946
|
+
"blob.core.windows.net",
|
|
26947
|
+
"azureedge.net",
|
|
26948
|
+
"azurewebsites.net",
|
|
26949
|
+
"githubusercontent.com",
|
|
26950
|
+
"github.io",
|
|
26951
|
+
"herokuapp.com",
|
|
26952
|
+
"vercel.app",
|
|
26953
|
+
"netlify.app",
|
|
26954
|
+
"pages.dev",
|
|
26955
|
+
"workers.dev",
|
|
26956
|
+
"trycloudflare.com",
|
|
26957
|
+
"ngrok.io",
|
|
26958
|
+
"ngrok-free.app",
|
|
26959
|
+
"onrender.com",
|
|
26960
|
+
"fly.dev"
|
|
26961
|
+
];
|
|
26962
|
+
/** Whether `host` has the shape of a host a product may list. */
|
|
26963
|
+
function isProductNetworkHost(host) {
|
|
26964
|
+
return PRODUCT_NETWORK_HOST_PATTERN.test(host);
|
|
26965
|
+
}
|
|
26966
|
+
/** Whether `host` is a domain anyone can host content on, listed whole. */
|
|
26967
|
+
function isSharedHostingDomain(host) {
|
|
26968
|
+
return SHARED_HOSTING_DOMAINS.includes(host);
|
|
26969
|
+
}
|
|
26970
|
+
strictObject({});
|
|
26971
|
+
const uuid = string$2().uuid();
|
|
26972
|
+
const sha256 = string$2().regex(/^[0-9a-f]{64}$/, "sha256 must be 64 lowercase hex characters");
|
|
26973
|
+
object({ jobId: string$2() });
|
|
26974
|
+
/** An upload's grant: a presigned PUT to staging and the headers it signed. */
|
|
26975
|
+
const uploadGrant = object({
|
|
26976
|
+
id: string$2(),
|
|
26977
|
+
url: string$2(),
|
|
26978
|
+
headers: record(string$2(), string$2()),
|
|
26979
|
+
expiresAt: string$2()
|
|
26980
|
+
});
|
|
26981
|
+
object({ version: number$1().int().min(1) });
|
|
26982
|
+
object({ documentId: string$2().uuid() });
|
|
26983
|
+
/** The uploads a product's move could not re-file, named while they fit the answer (PRO-3497). */
|
|
26984
|
+
const failedDocuments = array(object({
|
|
26985
|
+
id: string$2(),
|
|
26986
|
+
fileName: string$2(),
|
|
26987
|
+
error: string$2()
|
|
26988
|
+
}));
|
|
26989
|
+
object({
|
|
26990
|
+
backlogItemId: string$2(),
|
|
26991
|
+
divergenceCount: number$1(),
|
|
26992
|
+
created: boolean()
|
|
26993
|
+
});
|
|
26994
|
+
/** The options of a policy fix: build it once planned, and refuse it when the violation was fixed since. */
|
|
26995
|
+
const policyFixOptions = {
|
|
26996
|
+
autoBuild: boolean().optional(),
|
|
26997
|
+
refuseIfFixedSinceAssessment: boolean().optional()
|
|
26998
|
+
};
|
|
26999
|
+
object({
|
|
27000
|
+
findings: number$1(),
|
|
27001
|
+
findingIds: array(string$2()).nullable()
|
|
27002
|
+
});
|
|
27003
|
+
/** An ignore's expiry, when it has one, is in the future. */
|
|
27004
|
+
const expiresInFuture = (expiresAt) => !expiresAt || new Date(expiresAt).getTime() > Date.now();
|
|
27005
|
+
const EXPIRY_IN_FUTURE = {
|
|
27006
|
+
message: "The ignore expiry must be in the future.",
|
|
27007
|
+
path: ["ignoreExpiresAt"]
|
|
27008
|
+
};
|
|
27009
|
+
const ignoreNote = string$2().trim().max(280).nullish();
|
|
27010
|
+
const ignoreExpiresAt = string$2().datetime({ offset: true }).nullish();
|
|
27011
|
+
const productDocumentType = _enum([...PRODUCT_DOCUMENT_TYPES, LEGACY_PRODUCT_INSTRUCTIONS_TYPE]).transform((type) => type === "project_instructions" ? "product_instructions" : type);
|
|
27012
|
+
const customDocumentStatus = _enum(CUSTOM_DOCUMENT_STATUSES);
|
|
27013
|
+
const cloudPreferences = object({
|
|
27014
|
+
iacTool: _enum([
|
|
27015
|
+
"terraform",
|
|
27016
|
+
"pulumi",
|
|
27017
|
+
"cdk",
|
|
27018
|
+
"none"
|
|
27019
|
+
]).nullable().optional(),
|
|
27020
|
+
stateBackend: _enum([
|
|
27021
|
+
"aws-s3",
|
|
27022
|
+
"gcs",
|
|
27023
|
+
"azurerm",
|
|
27024
|
+
"tfcloud",
|
|
27025
|
+
"other"
|
|
27026
|
+
]).nullable().optional(),
|
|
27027
|
+
ciSystem: _enum([
|
|
27028
|
+
"github-actions",
|
|
27029
|
+
"gitlab-ci",
|
|
27030
|
+
"circleci",
|
|
27031
|
+
"jenkins",
|
|
27032
|
+
"other"
|
|
27033
|
+
]).nullable().optional(),
|
|
27034
|
+
authMethod: _enum([
|
|
27035
|
+
"oidc",
|
|
27036
|
+
"static-keys",
|
|
27037
|
+
"unknown"
|
|
27038
|
+
]).optional(),
|
|
27039
|
+
accountIsolation: _enum([
|
|
27040
|
+
"per-env",
|
|
27041
|
+
"shared",
|
|
27042
|
+
"unknown"
|
|
27043
|
+
]).optional()
|
|
27044
|
+
}).optional();
|
|
27045
|
+
const cloudBestPractices = object({
|
|
27046
|
+
dedicatedAccount: boolean().optional(),
|
|
27047
|
+
oidcAuth: boolean().optional(),
|
|
27048
|
+
regionMatchesPolicy: boolean().optional(),
|
|
27049
|
+
stateInSameAccount: boolean().optional()
|
|
27050
|
+
}).optional();
|
|
27051
|
+
const cloudProviderConfig = discriminatedUnion("provider", [
|
|
27052
|
+
object({
|
|
27053
|
+
provider: literal("aws"),
|
|
27054
|
+
accountId: string$2().regex(/^\d{12}$/, "AWS account ID must be 12 digits").optional(),
|
|
27055
|
+
region: string$2().min(1),
|
|
27056
|
+
preferences: cloudPreferences,
|
|
27057
|
+
bestPractices: cloudBestPractices
|
|
27058
|
+
}),
|
|
27059
|
+
object({
|
|
27060
|
+
provider: literal("gcp"),
|
|
27061
|
+
projectId: string$2().regex(/^[a-z][a-z0-9-]{4,28}[a-z0-9]$/, "Invalid GCP project ID").optional(),
|
|
27062
|
+
region: string$2().min(1),
|
|
27063
|
+
preferences: cloudPreferences,
|
|
27064
|
+
bestPractices: cloudBestPractices
|
|
27065
|
+
}),
|
|
27066
|
+
object({
|
|
27067
|
+
provider: literal("azure"),
|
|
27068
|
+
subscriptionId: string$2().uuid("Azure subscription ID must be a UUID").optional(),
|
|
27069
|
+
tenantId: string$2().uuid("Azure tenant ID must be a UUID").optional(),
|
|
27070
|
+
region: string$2().min(1),
|
|
27071
|
+
preferences: cloudPreferences,
|
|
27072
|
+
bestPractices: cloudBestPractices
|
|
27073
|
+
}),
|
|
27074
|
+
object({
|
|
27075
|
+
provider: literal("upcloud"),
|
|
27076
|
+
zone: string$2().min(1),
|
|
27077
|
+
username: string$2().min(1).optional(),
|
|
27078
|
+
authMethod: _enum(["password", "token"]).optional(),
|
|
27079
|
+
preferences: cloudPreferences,
|
|
27080
|
+
bestPractices: cloudBestPractices
|
|
27081
|
+
})
|
|
27082
|
+
]);
|
|
27083
|
+
const columnIndex = number$1().int().min(1).max(EXCEL_MAX_COLUMN);
|
|
27084
|
+
const sheetMap = object({
|
|
27085
|
+
sheetName: string$2().min(1).max(120),
|
|
27086
|
+
idColumn: columnIndex,
|
|
27087
|
+
questionColumn: columnIndex,
|
|
27088
|
+
detailColumns: array(columnIndex).max(20).optional(),
|
|
27089
|
+
sectionColumn: columnIndex.optional(),
|
|
27090
|
+
answerColumn: columnIndex,
|
|
27091
|
+
justificationColumn: columnIndex.optional(),
|
|
27092
|
+
firstDataRow: number$1().int().min(1).max(EXCEL_MAX_ROW)
|
|
27093
|
+
});
|
|
27094
|
+
const answerValue = string$2().trim().min(1).max(64);
|
|
27095
|
+
const justification = string$2().trim().max(2e3);
|
|
27096
|
+
const notApplicableReason = string$2().trim().min(1).max(1200);
|
|
27097
|
+
strictObject({ comment: string$2().trim().min(1).max(PLAN_REJECTION_COMMENT_MAX_CHARS) });
|
|
27098
|
+
strictObject({ expectedRunId: uuid.optional() }), strictObject({ force: boolean().optional() });
|
|
27099
|
+
strictObject({
|
|
27100
|
+
notes: string$2().trim().min(1).max(CHANGE_REQUEST_MAX_CHARS),
|
|
27101
|
+
/** Show the revised plan before the run continues (PRO-3164). */
|
|
27102
|
+
reviewPlan: boolean().optional(),
|
|
27103
|
+
/** The repository whose pull request should change, by its full name, for a run in several (PRO-3346). */
|
|
27104
|
+
repository: string$2().trim().min(1).max(255).optional()
|
|
27105
|
+
}), object({ steer: _enum([
|
|
27106
|
+
"replanning",
|
|
27107
|
+
"after_step",
|
|
27108
|
+
"after_round",
|
|
27109
|
+
"held",
|
|
27110
|
+
"sent"
|
|
27111
|
+
]).optional() }), object({ files: array(object({
|
|
27112
|
+
id: string$2(),
|
|
27113
|
+
url: string$2().url()
|
|
27114
|
+
})) });
|
|
27115
|
+
object({ images: array(object({
|
|
27116
|
+
name: string$2(),
|
|
27117
|
+
url: string$2().url().nullable()
|
|
27118
|
+
})) });
|
|
27119
|
+
strictObject({
|
|
27120
|
+
exit: _enum(REQUEST_EXITS),
|
|
27121
|
+
comment: string$2().trim().min(1).max(PLAN_REJECTION_COMMENT_MAX_CHARS).optional(),
|
|
27122
|
+
force: boolean().optional(),
|
|
27123
|
+
seen: array(string$2().uuid()).max(REVIEW_SEEN_MAX).optional()
|
|
27124
|
+
}).refine((answer) => answer.exit === "reject" === (answer.comment !== void 0), {
|
|
27125
|
+
message: "A rejection takes a comment for the planner, and no other answer takes one.",
|
|
27126
|
+
path: ["comment"]
|
|
27127
|
+
}).refine((answer) => answer.exit === "replan" || answer.force === void 0, {
|
|
27128
|
+
message: "Only Plan again can plan anyway.",
|
|
27129
|
+
path: ["force"]
|
|
27130
|
+
}).refine((answer) => answer.exit === "reviewed" || answer.seen === void 0, {
|
|
27131
|
+
message: "Only Mark reviewed says what was shown.",
|
|
27132
|
+
path: ["seen"]
|
|
27133
|
+
});
|
|
27134
|
+
strictObject({ type: productDocumentType }), object({
|
|
27135
|
+
version: number$1().int().min(1),
|
|
27136
|
+
unknownCitations: object({
|
|
27137
|
+
shown: array(string$2()),
|
|
27138
|
+
count: number$1().int().min(0)
|
|
27139
|
+
})
|
|
27140
|
+
}), strictObject({ type: productDocumentType }), strictObject({ type: productDocumentType }), strictObject({}), object({ started: number$1().int().min(0) }), strictObject({
|
|
27141
|
+
githubRepositoryId: uuid.optional(),
|
|
27142
|
+
/** A GitLab project, by the connection whose bot reaches it and GitLab's numeric id (P-PRO-150). */
|
|
27143
|
+
gitlabConnectionId: uuid.optional(),
|
|
27144
|
+
gitlabProjectId: number$1().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),
|
|
27145
|
+
provider: _enum(VCS_PROVIDERS).optional().default("github"),
|
|
27146
|
+
repositoryName: string$2().min(1).max(512).optional(),
|
|
27147
|
+
repositoryUrl: string$2().url().max(2048).optional(),
|
|
27148
|
+
repositoryDefaultBranch: string$2().max(255).optional().default("main"),
|
|
27149
|
+
/** The link this one replaces, retired in the same transaction (PRO-2522). */
|
|
27150
|
+
replacesRepositoryId: uuid.optional()
|
|
27151
|
+
}).refine((b) => b.gitlabConnectionId === void 0 === (b.gitlabProjectId === void 0), { message: "gitlabConnectionId and gitlabProjectId go together" }).refine((b) => b.gitlabConnectionId === void 0 || b.githubRepositoryId === void 0, { message: "A link is to a GitHub repository or a GitLab project, not both" }).refine((b) => b.githubRepositoryId === void 0 || linksGitHubAppRepositories(b.provider), { message: "githubRepositoryId is a GitHub repository; provider must be github" }), strictObject({
|
|
27152
|
+
repositoryId: uuid,
|
|
27153
|
+
baseBranch: string$2().trim().min(1).max(255).nullable()
|
|
27154
|
+
}), object({
|
|
27155
|
+
effectiveBranch: string$2(),
|
|
27156
|
+
protection: object({
|
|
27157
|
+
protected: boolean(),
|
|
27158
|
+
requiredStatusChecks: array(string$2()),
|
|
27159
|
+
enforcementLevel: string$2().nullable()
|
|
27160
|
+
}).nullable()
|
|
27161
|
+
}), strictObject({
|
|
27162
|
+
license: string$2().trim().min(1).max(MAINTENANCE_LICENSE_FILTER_MAX),
|
|
27163
|
+
ignoreReason: _enum(MAINTENANCE_IGNORE_REASONS),
|
|
27164
|
+
ignoreNote,
|
|
27165
|
+
ignoreExpiresAt
|
|
27166
|
+
}).refine((args) => expiresInFuture(args.ignoreExpiresAt), EXPIRY_IN_FUTURE), strictObject({
|
|
27167
|
+
license: string$2().trim().min(1).max(MAINTENANCE_LICENSE_FILTER_MAX),
|
|
27168
|
+
findingIds: array(uuid).min(1).optional()
|
|
27169
|
+
}), strictObject({ license: string$2().trim().min(1).max(MAINTENANCE_LICENSE_FILTER_MAX) }), object({
|
|
27170
|
+
backlogItemIds: array(string$2()),
|
|
27171
|
+
findingCount: number$1()
|
|
27172
|
+
}), strictObject({}), object({
|
|
27173
|
+
backlogItemId: string$2(),
|
|
27174
|
+
created: boolean()
|
|
27175
|
+
}), strictObject({ host: string$2().trim().toLowerCase().refine(isProductNetworkHost, { message: "A host is one exact DNS name, such as api.example.com: no wildcard, scheme, port or path" }).refine((host) => !isSharedHostingDomain(host), { message: "Anyone can host content on this domain, so it cannot be listed whole; list the one host you need under it" }) }), strictObject({
|
|
27176
|
+
type: _enum(AGENT_JOB_TYPES),
|
|
27177
|
+
documentType: string$2().max(30).optional(),
|
|
27178
|
+
priority: number$1().int().min(0).max(99).optional(),
|
|
27179
|
+
metadata: record(string$2(), unknown$1()).optional(),
|
|
27180
|
+
/** Which implementation should serve it (PRO-1332): a request the server may refuse. */
|
|
27181
|
+
runtime: _enum(AGENT_RUNTIMES).optional()
|
|
27182
|
+
}), strictObject({
|
|
27183
|
+
fileName: string$2().trim().min(1).max(1024),
|
|
27184
|
+
sizeBytes: number$1().int().nonnegative(),
|
|
27185
|
+
sha256
|
|
27186
|
+
}), object({ upload: uploadGrant }), strictObject({ uploadId: uuid }), object({
|
|
27187
|
+
questionnaireId: string$2(),
|
|
27188
|
+
jobId: string$2()
|
|
27189
|
+
}), strictObject({ mode: _enum(["generate", "republish"]).default("generate") }), strictObject({ docTypes: array(_enum(REALITY_DERIVED_DOCUMENT_TYPES)).min(1).max(REALITY_DERIVED_DOCUMENT_TYPES.length).optional() }), object({ jobIds: array(string$2()) }), object({ cancelledJobIds: array(string$2()) }), strictObject({
|
|
27190
|
+
factoryId: uuid,
|
|
27191
|
+
stopLine: boolean().optional()
|
|
27192
|
+
}), object({
|
|
27193
|
+
changed: boolean(),
|
|
27194
|
+
previousFactoryId: string$2().nullable(),
|
|
27195
|
+
lineStopped: boolean(),
|
|
27196
|
+
failedDocuments,
|
|
27197
|
+
/** How many uploads kept the old factory; null while not known, never read as none. */
|
|
27198
|
+
failedCount: number$1().int().min(0).nullable()
|
|
27199
|
+
}), strictObject({ documentIds: array(uuid).min(1).max(100).optional() }), object({
|
|
27200
|
+
rescopedCount: number$1().int().min(0).nullable(),
|
|
27201
|
+
failedDocuments,
|
|
27202
|
+
failedCount: number$1().int().min(0).nullable()
|
|
27203
|
+
}), strictObject({ repositoryId: uuid }), strictObject({
|
|
27204
|
+
ecosystem: string$2().min(1).max(50),
|
|
27205
|
+
packageName: string$2().min(1).max(255),
|
|
27206
|
+
/** The installed version, for a package resolved at several in a monorepo. */
|
|
27207
|
+
currentVersion: string$2().min(1).max(100).nullish(),
|
|
27208
|
+
/** The linked repository the group belongs to: a fix touches one. */
|
|
27209
|
+
productRepositoryId: uuid.nullish(),
|
|
27210
|
+
/** Build the fix as soon as it is planned (PRO-949). */
|
|
27211
|
+
autoBuild: boolean().optional()
|
|
27212
|
+
}), object({
|
|
27213
|
+
backlogItemId: string$2(),
|
|
27214
|
+
findingCount: number$1()
|
|
27215
|
+
}), strictObject({
|
|
27216
|
+
...policyFixOptions,
|
|
27217
|
+
controlId: string$2().min(1).max(40)
|
|
27218
|
+
}), strictObject({
|
|
27219
|
+
controlId: string$2().min(1).max(40),
|
|
27220
|
+
validMonths: union([
|
|
27221
|
+
literal(3),
|
|
27222
|
+
literal(6),
|
|
27223
|
+
literal(12)
|
|
27224
|
+
]),
|
|
27225
|
+
note: string$2().trim().max(1e3).optional()
|
|
27226
|
+
}), strictObject({ environments: array(string$2()).default(["development"]) }), strictObject({
|
|
27227
|
+
connection: object({
|
|
27228
|
+
name: string$2().min(1).max(150),
|
|
27229
|
+
provider: _enum(CLOUD_PROVIDERS),
|
|
27230
|
+
providerConfig: cloudProviderConfig
|
|
27231
|
+
}).refine((connection) => connection.provider === connection.providerConfig.provider, {
|
|
27232
|
+
message: "connection.provider must match connection.providerConfig.provider",
|
|
27233
|
+
path: ["providerConfig", "provider"]
|
|
27234
|
+
}),
|
|
27235
|
+
environment: object({
|
|
27236
|
+
name: string$2().min(1).max(100),
|
|
27237
|
+
slug: string$2().min(1).max(63).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Slug must be lowercase letters, digits, and dashes"),
|
|
27238
|
+
type: _enum(ENVIRONMENT_TYPES),
|
|
27239
|
+
ordinal: number$1().int().min(0).default(0),
|
|
27240
|
+
isProtected: boolean().default(false)
|
|
27241
|
+
}).optional()
|
|
27242
|
+
});
|
|
27243
|
+
object({
|
|
27244
|
+
url: string$2().url(),
|
|
27245
|
+
expiresAt: string$2()
|
|
27246
|
+
});
|
|
27247
|
+
object({
|
|
27248
|
+
opens: number$1().int().nonnegative(),
|
|
27249
|
+
closes: number$1().int().nonnegative(),
|
|
27250
|
+
products: array(object({
|
|
27251
|
+
productId: string$2().uuid(),
|
|
27252
|
+
opens: number$1().int().nonnegative(),
|
|
27253
|
+
closes: number$1().int().nonnegative()
|
|
27254
|
+
}))
|
|
27255
|
+
});
|
|
27256
|
+
object({
|
|
27257
|
+
status: customDocumentStatus.nullable(),
|
|
27258
|
+
/** The retry itself threw; absent when it went through. */
|
|
27259
|
+
error: string$2().optional()
|
|
27260
|
+
});
|
|
27261
|
+
/** A factory document type, its legacy name read as the current one. */
|
|
27262
|
+
const factoryDocumentType = _enum([...FACTORY_DOCUMENT_TYPES, LEGACY_FACTORY_INSTRUCTIONS_TYPE]).transform((type) => type === "team_instructions" ? FACTORY_INSTRUCTIONS_TYPE : type);
|
|
27263
|
+
strictObject({ type: factoryDocumentType }), strictObject({ type: factoryDocumentType });
|
|
27264
|
+
strictObject({ type: _enum(TENANT_DOCUMENT_TYPES) }), strictObject({ type: _enum(TENANT_DOCUMENT_TYPES).refine((type) => type !== "design_standards", { message: "The design standards draft is not made through sync" }) }), strictObject({
|
|
27265
|
+
name: string$2().min(1).max(50),
|
|
27266
|
+
action: _enum(["accept", "decline"])
|
|
27267
|
+
}), object({
|
|
27268
|
+
applied: _enum([
|
|
27269
|
+
"draft",
|
|
27270
|
+
"published",
|
|
27271
|
+
"declined"
|
|
27272
|
+
]),
|
|
27273
|
+
version: number$1().int().nullable()
|
|
27274
|
+
}), strictObject({
|
|
27275
|
+
fileName: string$2().trim().min(1).max(255),
|
|
27276
|
+
mime: string$2().min(1).max(255),
|
|
27277
|
+
sizeBytes: number$1().int().positive(),
|
|
27278
|
+
sha256,
|
|
27279
|
+
factoryId: string$2().optional(),
|
|
27280
|
+
productId: string$2().optional(),
|
|
27281
|
+
origin: string$2().optional()
|
|
27282
|
+
}), discriminatedUnion("deduplicated", [object({
|
|
27283
|
+
deduplicated: literal(true),
|
|
27284
|
+
documentId: string$2(),
|
|
27285
|
+
factoryId: string$2().nullable(),
|
|
27286
|
+
status: customDocumentStatus
|
|
27287
|
+
}), object({
|
|
27288
|
+
deduplicated: literal(false),
|
|
27289
|
+
upload: uploadGrant
|
|
27290
|
+
})]), strictObject({ uploadId: uuid }), object({
|
|
27291
|
+
documentId: string$2(),
|
|
27292
|
+
factoryId: string$2().nullable(),
|
|
27293
|
+
deduplicated: boolean(),
|
|
27294
|
+
status: customDocumentStatus.nullable(),
|
|
27295
|
+
/** Filing it threw; absent when it went through. */
|
|
27296
|
+
error: string$2().optional()
|
|
27297
|
+
}), strictObject({ ids: array(uuid).min(1).max(100) }), object({
|
|
27298
|
+
failedIds: array(string$2()).nullable(),
|
|
27299
|
+
deletedIds: array(string$2()).optional()
|
|
27300
|
+
}), strictObject({ ids: array(uuid).min(1).max(25) }), object({ referenced: array(string$2()) }), strictObject({
|
|
27301
|
+
approve: array(string$2().min(1).max(100)).max(200).default([]),
|
|
27302
|
+
remove: array(string$2().min(1).max(100)).max(200).default([])
|
|
27303
|
+
}).refine((b) => b.approve.length + b.remove.length > 0, { message: "Name a license to approve or remove" }), object({ version: number$1().nullable() }), strictObject({
|
|
27304
|
+
approve: string$2().trim().min(1).max(100).optional(),
|
|
27305
|
+
remove: string$2().trim().min(1).max(100).optional()
|
|
27306
|
+
}).refine((a) => Boolean(a.approve) !== Boolean(a.remove), { message: "Name one license to approve or remove" }), object({
|
|
27307
|
+
enrolled: number$1().int().nonnegative(),
|
|
27308
|
+
total: number$1().int().nonnegative()
|
|
27309
|
+
});
|
|
27310
|
+
strictObject({ kind: _enum(["sbom", "notices"]) }), object({
|
|
27311
|
+
url: string$2().url().nullable(),
|
|
27312
|
+
expiresAt: string$2().nullable()
|
|
27313
|
+
});
|
|
27314
|
+
strictObject({
|
|
27315
|
+
ignoreReason: _enum(MAINTENANCE_IGNORE_REASONS),
|
|
27316
|
+
ignoreNote,
|
|
27317
|
+
ignoreExpiresAt
|
|
27318
|
+
}).refine((args) => expiresInFuture(args.ignoreExpiresAt), EXPIRY_IN_FUTURE), strictObject({ autoBuild: boolean().optional() }), object({ backlogItemId: string$2() });
|
|
27319
|
+
strictObject(policyFixOptions);
|
|
27320
|
+
strictObject({ tier: _enum([
|
|
27321
|
+
"development",
|
|
27322
|
+
"staging",
|
|
27323
|
+
"production"
|
|
27324
|
+
]) });
|
|
27325
|
+
object({
|
|
27326
|
+
url: string$2().url(),
|
|
27327
|
+
expiresAt: string$2(),
|
|
27328
|
+
written: number$1(),
|
|
27329
|
+
unwrittenCount: number$1(),
|
|
27330
|
+
summary: object({
|
|
27331
|
+
byTaiga: number$1(),
|
|
27332
|
+
byYou: number$1(),
|
|
27333
|
+
notApplicable: number$1(),
|
|
27334
|
+
blank: number$1()
|
|
27335
|
+
})
|
|
27336
|
+
}), strictObject({
|
|
27337
|
+
columnMap: object({ sheets: array(sheetMap).min(1).max(50) }),
|
|
27338
|
+
answerVocabulary: array(string$2().min(1).max(64)).max(16).nullable().optional(),
|
|
27339
|
+
answerLegend: string$2().max(4e3).nullable().optional()
|
|
27340
|
+
}), object({
|
|
27341
|
+
imported: number$1(),
|
|
27342
|
+
skippedRows: number$1()
|
|
27343
|
+
});
|
|
27344
|
+
discriminatedUnion("action", [
|
|
27345
|
+
strictObject({ action: literal("accept") }),
|
|
27346
|
+
strictObject({ action: literal("reject") }),
|
|
27347
|
+
strictObject({
|
|
27348
|
+
action: literal("edit"),
|
|
27349
|
+
answerValue: answerValue.optional(),
|
|
27350
|
+
justification: justification.optional()
|
|
27351
|
+
}),
|
|
27352
|
+
strictObject({
|
|
27353
|
+
action: literal("answer"),
|
|
27354
|
+
answerValue,
|
|
27355
|
+
justification: justification.optional()
|
|
27356
|
+
}),
|
|
27357
|
+
strictObject({
|
|
27358
|
+
action: literal("not_applicable"),
|
|
27359
|
+
/** The token the form uses for it, when it has one -- `NA`, `N/A`, `es`. */
|
|
27360
|
+
answerValue: answerValue.optional(),
|
|
27361
|
+
reason: notApplicableReason
|
|
27362
|
+
})
|
|
27363
|
+
]);
|
|
27364
|
+
//#endregion
|
|
26439
27365
|
//#region ../../packages/types/dist/auto-build.js
|
|
26440
27366
|
/**
|
|
26441
27367
|
* Auto-build: the review-plans gate (PRO-1305).
|
|
@@ -26520,30 +27446,6 @@ Object.freeze({
|
|
|
26520
27446
|
applicationOwnership: true
|
|
26521
27447
|
});
|
|
26522
27448
|
//#endregion
|
|
26523
|
-
//#region ../../packages/types/dist/product-network.js
|
|
26524
|
-
/**
|
|
26525
|
-
* Where an engineer build's sandbox may connect to (PRO-2793, part of
|
|
26526
|
-
* PRO-1980).
|
|
26527
|
-
*
|
|
26528
|
-
* The engineer pod's credential gateway is the sandbox's only way out. By
|
|
26529
|
-
* default it lets through a standard list: the public package registries,
|
|
26530
|
-
* toolchain downloads, container registries, GitHub for reading public
|
|
26531
|
-
* dependencies, and the tenant's own registries. A product adds to that:
|
|
26532
|
-
*
|
|
26533
|
-
* - `allowlist` (the default, also what `null` means): the standard list plus
|
|
26534
|
-
* the product's own `networkHosts`.
|
|
26535
|
-
* - `unrestricted`: any public host. For a product whose builds need hosts
|
|
26536
|
-
* nobody can list in advance; the gateway still refuses private and
|
|
26537
|
-
* metadata addresses, and still never hands the sandbox a credential.
|
|
26538
|
-
*
|
|
26539
|
-
* A host is one exact name (`api.example.com`). There are no wildcards: a
|
|
26540
|
-
* wildcard over a multi-label public suffix (`*.co.uk`) or a provider's
|
|
26541
|
-
* namespace would admit every tenant under it, and telling those apart needs
|
|
26542
|
-
* the public suffix list. A build that needs many names under one domain is
|
|
26543
|
-
* what `unrestricted` is for.
|
|
26544
|
-
*/
|
|
26545
|
-
const PRODUCT_NETWORK_ACCESS = ["allowlist", "unrestricted"];
|
|
26546
|
-
//#endregion
|
|
26547
27449
|
//#region src/compat.ts
|
|
26548
27450
|
/**
|
|
26549
27451
|
* Why this server should not serve the API it reached, or null when it may
|
|
@@ -26611,6 +27513,7 @@ const TOOLSET_OF = {
|
|
|
26611
27513
|
taiga_fix_finding: "operate",
|
|
26612
27514
|
taiga_fix_policy_violation: "operate",
|
|
26613
27515
|
taiga_ignore_finding: "operate",
|
|
27516
|
+
taiga_allow_license: "operate",
|
|
26614
27517
|
taiga_resolve_action: "operate",
|
|
26615
27518
|
taiga_create_factory: "setup",
|
|
26616
27519
|
taiga_rename_factory: "setup",
|
|
@@ -26635,6 +27538,7 @@ const TOOLSET_OF = {
|
|
|
26635
27538
|
taiga_pause_line: "setup",
|
|
26636
27539
|
taiga_create_environment: "setup",
|
|
26637
27540
|
taiga_update_environment: "setup",
|
|
27541
|
+
taiga_ship_notices: "setup",
|
|
26638
27542
|
taiga_get_deployments: "observe",
|
|
26639
27543
|
taiga_get_monitoring: "observe",
|
|
26640
27544
|
taiga_get_delivery_metrics: "observe",
|
|
@@ -26664,6 +27568,40 @@ const TOOLSET_OF = {
|
|
|
26664
27568
|
taiga_retry_knowledge: "documents"
|
|
26665
27569
|
};
|
|
26666
27570
|
/**
|
|
27571
|
+
* Deliver & Operate's tools (PRO-3834): initiatives, plans, runs and the line, fixes, environments, deployments,
|
|
27572
|
+
* monitoring and delivery metrics. An organization whose plan refuses delivery work does not see them, as its web app
|
|
27573
|
+
* shows none of their views; the API says which (`deliveryIncluded` at introspection), so the plan, not the key,
|
|
27574
|
+
* decides. Fixing a finding plans and builds, so it goes with them.
|
|
27575
|
+
*/
|
|
27576
|
+
const DELIVERY_TOOLS = /* @__PURE__ */ new Set([
|
|
27577
|
+
"taiga_list_initiatives",
|
|
27578
|
+
"taiga_get_initiative",
|
|
27579
|
+
"taiga_get_plan",
|
|
27580
|
+
"taiga_list_runs",
|
|
27581
|
+
"taiga_get_run",
|
|
27582
|
+
"taiga_get_line",
|
|
27583
|
+
"taiga_queue_initiative",
|
|
27584
|
+
"taiga_move_initiative",
|
|
27585
|
+
"taiga_review_plan",
|
|
27586
|
+
"taiga_plan_again",
|
|
27587
|
+
"taiga_resume_initiative",
|
|
27588
|
+
"taiga_stop_run",
|
|
27589
|
+
"taiga_request_changes",
|
|
27590
|
+
"taiga_request_initiatives",
|
|
27591
|
+
"taiga_fix_finding",
|
|
27592
|
+
"taiga_fix_policy_violation",
|
|
27593
|
+
"taiga_ship_notices",
|
|
27594
|
+
"taiga_set_auto_build",
|
|
27595
|
+
"taiga_set_auto_merge",
|
|
27596
|
+
"taiga_pause_line",
|
|
27597
|
+
"taiga_create_environment",
|
|
27598
|
+
"taiga_update_environment",
|
|
27599
|
+
"taiga_get_deployments",
|
|
27600
|
+
"taiga_get_monitoring",
|
|
27601
|
+
"taiga_get_delivery_metrics",
|
|
27602
|
+
"taiga_sync_design_tokens"
|
|
27603
|
+
]);
|
|
27604
|
+
/**
|
|
26667
27605
|
* TAIGA_TOOLSETS as written: comma-separated names, or "all"; a name it does not know is said, not ignored, and so
|
|
26668
27606
|
* is a value that names none.
|
|
26669
27607
|
*/
|
|
@@ -26894,7 +27832,7 @@ function setupNextStep(product) {
|
|
|
26894
27832
|
if (isImportedProduct(product)) switch (importStatusOf(product)) {
|
|
26895
27833
|
case "analyzing":
|
|
26896
27834
|
case "generating_docs": return `The import is running and derives the documents from the code (taiga_wait (jobId) follows its job). Then review them, and ${FINISH_DISCOVERY}`;
|
|
26897
|
-
case "complete": return "The import is done: review the reality documents (taiga_get_product_document without type), then
|
|
27835
|
+
case "complete": return "The import is done: review the reality documents (taiga_get_product_document without type), then " + FINISH_DISCOVERY;
|
|
26898
27836
|
case "failed":
|
|
26899
27837
|
case "cancelled": return "The last import did not finish: taiga_start_import runs it again.";
|
|
26900
27838
|
default: return "Link its repository if it has none (taiga_list_linkable_repositories, taiga_link_repository), then taiga_start_import: the import derives the documents from the code and runs by itself.";
|
|
@@ -26902,7 +27840,6 @@ function setupNextStep(product) {
|
|
|
26902
27840
|
if (product.discoveryStep === void 0) return null;
|
|
26903
27841
|
switch (product.discoveryStep) {
|
|
26904
27842
|
case "documents": return `The specification is done; the other documents are generated from it (taiga_generate_product_documents starts the ones still missing). Once every required one is published, ${FINISH_DISCOVERY}`;
|
|
26905
|
-
case "checks": return `The documents are published; the checks are stamped in the web, then ${FINISH_DISCOVERY}`;
|
|
26906
27843
|
default: return `Discovery is a conversation held in the web: the person describes the product there and the specification comes out of it. The other documents follow, then ${FINISH_DISCOVERY}`;
|
|
26907
27844
|
}
|
|
26908
27845
|
}
|
|
@@ -26931,8 +27868,16 @@ const BY_ARGUMENT = {
|
|
|
26931
27868
|
knowledgeId: "taiga_list_knowledge lists the knowledge files.",
|
|
26932
27869
|
productId: "taiga_list_products lists the products the key can see; a name is not an ID (taiga_get_product takes a slug)."
|
|
26933
27870
|
};
|
|
27871
|
+
/**
|
|
27872
|
+
* A licence a product does not hold, as against a product the key cannot see
|
|
27873
|
+
* (PRO-3879). Any other 404 (the routes do not exist where the licenses
|
|
27874
|
+
* feature is off) blames neither.
|
|
27875
|
+
*/
|
|
27876
|
+
const licenseMiss = (hint) => (said) => /\blicen[cs]e\b/i.test(said) ? hint : /\b(product|project) not found/i.test(said) ? BY_ARGUMENT["productId"] : void 0;
|
|
26934
27877
|
/** Where an argument means something else on one tool, or the tool's 404 needs its own word. */
|
|
26935
27878
|
const BY_TOOL = {
|
|
27879
|
+
taiga_allow_license: licenseMiss("taiga_list_packages (productId) summarizes the product's licenses as its findings name them; a license with no finding there has nothing to allow or reopen."),
|
|
27880
|
+
taiga_list_licenses: licenseMiss("taiga_list_licenses without license lists the licenses in use, and taiga_list_packages (productId) one product's, spelled as their findings spell them."),
|
|
26936
27881
|
taiga_resolve_action: () => "taiga_list_action_required lists the items with their IDs.",
|
|
26937
27882
|
taiga_get_sbom: () => "An SBOM exists once a sweep has inventoried that repository, where the licenses feature is enabled; taiga_list_packages (linkId) says whether it has packages, and taiga_list_product_repositories lists the links.",
|
|
26938
27883
|
taiga_get_notices: () => "Notices exist once a sweep has inventoried that repository, where the licenses feature is enabled; taiga_list_product_repositories lists the links.",
|
|
@@ -27409,6 +28354,8 @@ const PACKAGE_ROW_FIELDS = [
|
|
|
27409
28354
|
"scopeReason",
|
|
27410
28355
|
"parentReason",
|
|
27411
28356
|
"sourcePath",
|
|
28357
|
+
"baseImage",
|
|
28358
|
+
"imageLayer",
|
|
27412
28359
|
"productRepositoryId",
|
|
27413
28360
|
"detectedAt"
|
|
27414
28361
|
];
|
|
@@ -27430,7 +28377,10 @@ const FINDING_ROW_FIELDS = [
|
|
|
27430
28377
|
"tier",
|
|
27431
28378
|
"kevListedAt",
|
|
27432
28379
|
"malicious",
|
|
28380
|
+
"baseMove",
|
|
28381
|
+
"baseImageMove",
|
|
27433
28382
|
"removal",
|
|
28383
|
+
"replaceable",
|
|
27434
28384
|
"status",
|
|
27435
28385
|
"title",
|
|
27436
28386
|
"ecosystem",
|
|
@@ -27559,11 +28509,19 @@ function withFixability(row) {
|
|
|
27559
28509
|
fixNote: reason === "not_shipped" ? "No longer applies: the package is only a build for platforms the repository does not deploy to. Nothing to do." : reason === "license_approved" ? "No longer applies: its license is on the organization's approved list. Nothing to do." : reason === "license_undecided" ? "No longer applies: its license can no longer be judged against the list (some of what it can stand for is approved). Nothing to do." : reason === "superseded" ? "Replaced by the repository's finding of its license, which lists every package under it. Nothing to do here." : "Resolved: the sweep no longer reports it. Nothing to do."
|
|
27560
28510
|
};
|
|
27561
28511
|
}
|
|
27562
|
-
if (row["kind"] === "license_violation") return {
|
|
28512
|
+
if (row["kind"] === "license_violation" && row["replaceable"] === true) return {
|
|
27563
28513
|
...row,
|
|
27564
|
-
fixable:
|
|
27565
|
-
fixNote: "
|
|
28514
|
+
fixable: true,
|
|
28515
|
+
fixNote: "taiga_fix_finding raises an initiative that replaces the packages under this license with ones whose license, and their own dependencies' licenses, are on the approved list. It never approves the license: that is taiga_change_license, or ignoring the finding."
|
|
27566
28516
|
};
|
|
28517
|
+
if (row["kind"] === "license_violation") {
|
|
28518
|
+
const status = String(row["status"]);
|
|
28519
|
+
return {
|
|
28520
|
+
...row,
|
|
28521
|
+
fixable: false,
|
|
28522
|
+
fixNote: status === "remediating" ? "Being fixed: an initiative replaces the packages under this license, and its plan may be waiting for a person's review. taiga_list_initiatives with query \"Replace the\" finds it; follow it there." : status === "open" ? "This Taiga does not replace the packages under a license yet, so taiga_fix_finding refuses it: approve the license (taiga_change_license) or ignore the finding." : `taiga_fix_finding takes only an open license finding, and this one is ${status}.`
|
|
28523
|
+
};
|
|
28524
|
+
}
|
|
27567
28525
|
const dependency = row["kind"] === "vulnerable_dependency" || row["kind"] === "outdated_dependency";
|
|
27568
28526
|
const signals = {
|
|
27569
28527
|
malicious: typeof row["malicious"] === "boolean" ? row["malicious"] : null,
|
|
@@ -27587,14 +28545,37 @@ function withFixability(row) {
|
|
|
27587
28545
|
fixable: true,
|
|
27588
28546
|
fixNote: "A malicious package: taiga_fix_finding removes it (or replaces it), with every advisory on that version."
|
|
27589
28547
|
} : row;
|
|
28548
|
+
if (typeof row["baseMove"] === "boolean" && isAgentRemediable({ remediableBy: typeof row["remediableBy"] === "string" ? row["remediableBy"] : null })) return row["baseMove"] === true && row["status"] === "open" ? {
|
|
28549
|
+
...row,
|
|
28550
|
+
fixable: true,
|
|
28551
|
+
fixNote: "taiga_fix_finding moves the base images it is in to the builds their tags name now, the FROM line alone, with every other advisory that move removes."
|
|
28552
|
+
} : {
|
|
28553
|
+
...row,
|
|
28554
|
+
fixable: false,
|
|
28555
|
+
fixNote: "Inside a base image no newer build of which removes it yet: it is fixed by a base move, not a package bump, and is held until one does."
|
|
28556
|
+
};
|
|
27590
28557
|
if (dependency && !row["recommendedVersion"] && row["remediableBy"] === "coding_agent") return {
|
|
27591
28558
|
...row,
|
|
27592
28559
|
fixable: false,
|
|
27593
|
-
fixNote: "
|
|
28560
|
+
fixNote: noVersionNote(row["data"])
|
|
27594
28561
|
};
|
|
27595
28562
|
return row;
|
|
27596
28563
|
}
|
|
27597
28564
|
/**
|
|
28565
|
+
* Why a dependency has no version to move to. A language runtime declared as
|
|
28566
|
+
* a container image moves only to a release the image has published
|
|
28567
|
+
* (PRO-3729), so its vendor's fix can be out while Fix is refused: the
|
|
28568
|
+
* finding's runtime data says so where the row carries it (taiga_get_finding).
|
|
28569
|
+
*/
|
|
28570
|
+
function noVersionNote(data) {
|
|
28571
|
+
const runtime = data?.runtime;
|
|
28572
|
+
const fixedIn = typeof runtime?.["fixedIn"] === "string" ? runtime["fixedIn"] : null;
|
|
28573
|
+
if (fixedIn) return `Fixed in ${fixedIn}, but there is no release to move to yet: the image the repository declares has not published it, or, where the line has ended, a supported line (the summary says which). taiga_fix_finding refuses it until there is one.`;
|
|
28574
|
+
if (runtime && (typeof runtime["newest"] === "string" || "endOfLife" in runtime)) return "A newer release is out, but the image the repository declares has not published one to move to yet (the summary names the image): taiga_fix_finding refuses it until it does.";
|
|
28575
|
+
if (runtime) return "No fixed release is known yet, or, where the line has ended, the image the repository declares has not published a supported line (the summary says which): taiga_fix_finding refuses it until there is one.";
|
|
28576
|
+
return "No version to move to yet: no fixed version is known, or for a language runtime the image the repository declares has not published one (taiga_get_finding says which). taiga_fix_finding refuses it until there is one.";
|
|
28577
|
+
}
|
|
28578
|
+
/**
|
|
27598
28579
|
* Dev-only, runtime or unknown, said on a dependency row (PRO-2861) in place of the raw per-lockfile groups
|
|
27599
28580
|
* the API carries: the groups need the lockfile name to read, which `dependencyScope` does once here.
|
|
27600
28581
|
*/
|
|
@@ -27650,7 +28631,10 @@ const ADVISORY_KEPT = [
|
|
|
27650
28631
|
"summary",
|
|
27651
28632
|
"details",
|
|
27652
28633
|
"severity",
|
|
27653
|
-
"
|
|
28634
|
+
"severitySource",
|
|
28635
|
+
"cvssScore",
|
|
28636
|
+
"published",
|
|
28637
|
+
"baseImage"
|
|
27654
28638
|
];
|
|
27655
28639
|
const ADVISORY_TEXT_LIMIT = 1500;
|
|
27656
28640
|
/** The packages of a licence finding shown without full: true (PRO-3394); the rest are counted. */
|
|
@@ -27691,6 +28675,36 @@ const SWEEP_FIELDS = [
|
|
|
27691
28675
|
"createdAt",
|
|
27692
28676
|
"completedAt"
|
|
27693
28677
|
];
|
|
28678
|
+
/**
|
|
28679
|
+
* The images a sweep's shipped images build on that it could not read, and
|
|
28680
|
+
* why (PRO-4073): a base nobody read is no clean base. Only where the API
|
|
28681
|
+
* returns the list (null with the containerImages flag off, or from a sweep
|
|
28682
|
+
* that listed none).
|
|
28683
|
+
*/
|
|
28684
|
+
const BASE_IMAGES_SHOWN = 10;
|
|
28685
|
+
function notReadBases(sweep) {
|
|
28686
|
+
const bases = sweep.baseImages ?? [];
|
|
28687
|
+
const notRead = bases.filter((b) => b.state === "not_read" || b.packages?.state === "not_read");
|
|
28688
|
+
const read = bases.flatMap((b) => b.packages?.state === "read" ? [{
|
|
28689
|
+
image: b.image,
|
|
28690
|
+
path: b.path,
|
|
28691
|
+
os: b.os ?? null,
|
|
28692
|
+
vulnerabilities: b.packages.vulnerabilities,
|
|
28693
|
+
removedByMoving: b.packages.candidate?.removes ?? 0,
|
|
28694
|
+
noDistroFix: b.packages.noDistroFix ?? null,
|
|
28695
|
+
movesTo: b.packages.candidate?.image ?? null
|
|
28696
|
+
}] : []);
|
|
28697
|
+
return {
|
|
28698
|
+
...read.length > 0 ? {
|
|
28699
|
+
baseImages: read.slice(0, BASE_IMAGES_SHOWN),
|
|
28700
|
+
...read.length > BASE_IMAGES_SHOWN ? { baseImagesCount: read.length } : {}
|
|
28701
|
+
} : {},
|
|
28702
|
+
...notRead.length > 0 ? {
|
|
28703
|
+
baseImagesNotRead: notRead.slice(0, BASE_IMAGES_SHOWN),
|
|
28704
|
+
...notRead.length > BASE_IMAGES_SHOWN ? { baseImagesNotReadCount: notRead.length } : {}
|
|
28705
|
+
} : {}
|
|
28706
|
+
};
|
|
28707
|
+
}
|
|
27694
28708
|
/** A policy violation in a list: what it is, how bad, and what is being done about it. */
|
|
27695
28709
|
const VIOLATION_FIELDS = [
|
|
27696
28710
|
"id",
|
|
@@ -27747,11 +28761,51 @@ function standingSummary(standing) {
|
|
|
27747
28761
|
resolvedViolations: resolved
|
|
27748
28762
|
};
|
|
27749
28763
|
}
|
|
28764
|
+
/**
|
|
28765
|
+
* How the backlog covers the controls that need a feature (PRO-3860), one row
|
|
28766
|
+
* each: the live initiatives that build it, or why it is deferred, and whether
|
|
28767
|
+
* nothing covers it. Empty before a backlog answered for any. It says what is
|
|
28768
|
+
* planned, never that a control is met.
|
|
28769
|
+
*/
|
|
28770
|
+
function controlCoverageList(controls) {
|
|
28771
|
+
return (Array.isArray(controls) ? controls : []).flatMap((control) => {
|
|
28772
|
+
const coverage = control["coverage"];
|
|
28773
|
+
if (!coverage || typeof coverage !== "object") return [];
|
|
28774
|
+
const initiatives = Array.isArray(coverage["initiatives"]) ? coverage["initiatives"] : [];
|
|
28775
|
+
return [{
|
|
28776
|
+
controlId: control["controlId"],
|
|
28777
|
+
state: control["state"],
|
|
28778
|
+
planned: plannedFor(coverage),
|
|
28779
|
+
uncovered: coverage["uncovered"] === true,
|
|
28780
|
+
...initiatives.length > 0 ? { initiatives: initiatives.map((item) => ({
|
|
28781
|
+
id: item["id"],
|
|
28782
|
+
title: item["title"]
|
|
28783
|
+
})) } : {},
|
|
28784
|
+
...coverage["deferredReason"] ? { deferredReason: coverage["deferredReason"] } : {}
|
|
28785
|
+
}];
|
|
28786
|
+
});
|
|
28787
|
+
}
|
|
28788
|
+
/**
|
|
28789
|
+
* Whether the backlog plans a control: yes when a live initiative builds it, later when it deferred it with a
|
|
28790
|
+
* reason, no otherwise. A deferred control reads "planned later" rather than only "not assessed" (PRO-3860).
|
|
28791
|
+
*/
|
|
28792
|
+
function plannedFor(coverage) {
|
|
28793
|
+
if (Array.isArray(coverage["initiatives"]) && coverage["initiatives"].length > 0) return "yes";
|
|
28794
|
+
return coverage["deferredReason"] ? "later" : "no";
|
|
28795
|
+
}
|
|
28796
|
+
/** The coverage, when the backlog answered for any control, for the default and full answers. */
|
|
28797
|
+
function controlCoveragePart(controls) {
|
|
28798
|
+
const coverage = controlCoverageList(controls);
|
|
28799
|
+
return coverage.length > 0 ? { controlCoverage: coverage } : {};
|
|
28800
|
+
}
|
|
27750
28801
|
const findingId = idInput("finding", "taiga_list_findings gives it");
|
|
27751
28802
|
/** Open findings by severity and kind, and the latest sweeps, as the first page of findings carries them. */
|
|
27752
28803
|
async function sweepOverview(client, productId) {
|
|
27753
28804
|
const [summary, sweeps] = await Promise.all([client.getMaintenanceSummary(productId), client.listMaintenanceSweeps(productId, { limit: 3 })]);
|
|
27754
|
-
const latest =
|
|
28805
|
+
const latest = sweeps.data.map((sweep) => ({
|
|
28806
|
+
...pickFields([sweep], SWEEP_FIELDS)[0],
|
|
28807
|
+
...notReadBases(sweep)
|
|
28808
|
+
}));
|
|
27755
28809
|
const { lastRun: _lastRun, lastRuns: _lastRuns, topFindings: _top, violatedControls: _controls, ...counts } = summary.data;
|
|
27756
28810
|
return {
|
|
27757
28811
|
...counts,
|
|
@@ -27759,6 +28813,20 @@ async function sweepOverview(client, productId) {
|
|
|
27759
28813
|
...latest.length === 0 ? { note: "This product has never been swept: no findings is not an all-clear." } : {}
|
|
27760
28814
|
};
|
|
27761
28815
|
}
|
|
28816
|
+
/** The organization's licence read takes an id this long at most, as `organizationLicenseQuery` does. */
|
|
28817
|
+
const ORGANIZATION_LICENSE_MAX = 200;
|
|
28818
|
+
/**
|
|
28819
|
+
* Why a license is allowed in a product, in a license's words, as the web's
|
|
28820
|
+
* Allow dialog offers them (PRO-3727); each is stored as the ignore reason
|
|
28821
|
+
* beside it.
|
|
28822
|
+
*/
|
|
28823
|
+
const LICENSE_ALLOW_REASON_TO_IGNORE = {
|
|
28824
|
+
accepted: "accepted_risk",
|
|
28825
|
+
not_handed_out: "not_reachable",
|
|
28826
|
+
no_alternative: "wont_fix",
|
|
28827
|
+
read_wrong: "false_positive"
|
|
28828
|
+
};
|
|
28829
|
+
const LICENSE_ALLOW_REASONS = Object.keys(LICENSE_ALLOW_REASON_TO_IGNORE);
|
|
27762
28830
|
function registerMaintenanceTools(server, client) {
|
|
27763
28831
|
if (client.hasCapability("tenant:settings")) server.registerTool("taiga_change_license", {
|
|
27764
28832
|
title: "Approve or remove a license",
|
|
@@ -27846,10 +28914,18 @@ function registerMaintenanceTools(server, client) {
|
|
|
27846
28914
|
}));
|
|
27847
28915
|
server.registerTool("taiga_list_licenses", {
|
|
27848
28916
|
title: "List the organization's licenses",
|
|
27849
|
-
description: "List the organization's approved license list and every license in use across its products, approved or not, with packages, products and repositories. With license, where that one is used: products, repositories
|
|
27850
|
-
inputSchema: object({
|
|
28917
|
+
description: "List the organization's approved license list and every license in use across its products, approved or not, with packages, products and repositories. With license, where that one is used: products, repositories, what brings its packages in, and its approved-list history. With license and productId, that license in one product: its standing against the list, what it asks, when to allow it there, how many packages ship, and what brings them in. Decide it there with taiga_allow_license. NOT_FOUND where the licenses feature is off.",
|
|
28918
|
+
inputSchema: object({
|
|
28919
|
+
license: string$2().trim().min(1).max(MAINTENANCE_LICENSE_FILTER_MAX).optional().describe("An SPDX license id or expression: where it is used, instead of the list"),
|
|
28920
|
+
productId: productId.optional().describe("With license: that license in this product only (taiga_list_products gives it)")
|
|
28921
|
+
}),
|
|
27851
28922
|
annotations: READ_ONLY
|
|
27852
|
-
}, (params) => runTool(async () =>
|
|
28923
|
+
}, (params) => runTool(async () => {
|
|
28924
|
+
if (params.productId !== void 0 && params.license === void 0) return toolError("VALIDATION_ERROR", "Name the license to read in this product (license).");
|
|
28925
|
+
if (params.license === void 0) return toolResult((await client.getOrganizationLicenses()).data);
|
|
28926
|
+
if (params.productId === void 0 && params.license.length > ORGANIZATION_LICENSE_MAX) return toolError("VALIDATION_ERROR", `A license id is at most ${ORGANIZATION_LICENSE_MAX} characters; name a product (productId) to read a longer expression.`);
|
|
28927
|
+
return toolResult(params.productId !== void 0 ? (await client.getProductLicense(params.productId, params.license)).data : (await client.getOrganizationLicense(params.license)).data);
|
|
28928
|
+
}));
|
|
27853
28929
|
server.registerTool("taiga_get_sbom", {
|
|
27854
28930
|
title: "Download a repository SBOM",
|
|
27855
28931
|
description: "Download one repository's software bill of materials (CycloneDX 1.6). Large; taiga_list_packages summarizes. NOT_FOUND where the licenses feature is off, or before a sweep has read the repository.",
|
|
@@ -27916,7 +28992,8 @@ function registerMaintenanceTools(server, client) {
|
|
|
27916
28992
|
const own = violations.filter((violation) => String(violation["controlId"]).toUpperCase() === wanted);
|
|
27917
28993
|
return toolResult({
|
|
27918
28994
|
...control,
|
|
27919
|
-
...control.state === "no_evidence" ? { meaning: "Not assessed: no finding or violation is recorded against it, and the documents were not found to show it met. That is no evidence either way, not a pass." } : {},
|
|
28995
|
+
...control.state === "no_evidence" ? { meaning: (control.coverage && plannedFor(control.coverage) === "later" ? `Planned later: the backlog deferred it (${String(control.coverage.deferredReason).replace(/\.$/, "")}). ` : "") + "Not assessed: no finding or violation is recorded against it, and the documents were not found to show it met. That is no evidence either way, not a pass." } : {},
|
|
28996
|
+
...control.coverage ? { coverageNote: "coverage is how the backlog plans for it: the live initiatives that build it, or why it is deferred. It says what is planned, not that the control is met." } : {},
|
|
27920
28997
|
...control.assessment == null && (control.state === "breached" || control.state === "diverged") ? { assessmentNote: "No met or not-applicable verdict is shown: an open finding or violation against the control outranks one." } : {},
|
|
27921
28998
|
...Number(control.findingCount ?? 0) > 0 ? { findingsNote: `${String(control.findingCount)} open scanner finding${control.findingCount === 1 ? "" : "s"} breach it; taiga_list_findings (productId) lists them with the controls each breaches. violations below are the documents' only.` } : {},
|
|
27922
28999
|
violations: pickFields(own, VIOLATION_FIELDS),
|
|
@@ -27939,6 +29016,7 @@ function registerMaintenanceTools(server, client) {
|
|
|
27939
29016
|
...wording,
|
|
27940
29017
|
...standing,
|
|
27941
29018
|
controls: listed,
|
|
29019
|
+
...controlCoveragePart(controls),
|
|
27942
29020
|
divergences: cutLongText(violations),
|
|
27943
29021
|
resolvedDivergences: cutLongText(resolvedAll.slice(0, FULL_RESOLVED)),
|
|
27944
29022
|
...resolvedAll.length > FULL_RESOLVED ? { resolvedNote: `The first ${FULL_RESOLVED} of ${resolvedAll.length} resolved violations.` } : {}
|
|
@@ -27962,7 +29040,8 @@ function registerMaintenanceTools(server, client) {
|
|
|
27962
29040
|
"findingsByTier",
|
|
27963
29041
|
"plannedCount"
|
|
27964
29042
|
]),
|
|
27965
|
-
violations: pickFields(violations, VIOLATION_FIELDS)
|
|
29043
|
+
violations: pickFields(violations, VIOLATION_FIELDS),
|
|
29044
|
+
...controlCoveragePart(controls)
|
|
27966
29045
|
});
|
|
27967
29046
|
}));
|
|
27968
29047
|
if (!client.hasCapability("products:write")) return;
|
|
@@ -27988,9 +29067,31 @@ function registerMaintenanceTools(server, client) {
|
|
|
27988
29067
|
const finding = (await client.updateFinding(params.findingId, body)).data;
|
|
27989
29068
|
return toolResult(findingRows([finding], ["ignoreNote"])[0]);
|
|
27990
29069
|
}));
|
|
29070
|
+
server.registerTool("taiga_allow_license", {
|
|
29071
|
+
title: "Allow or reopen a license in a product",
|
|
29072
|
+
description: "Allow a license in one product without approving it for the organization: every license finding of it there that is open, or whose ignore lapsed, is ignored with one reason, as the web's Allow in this product does; one already ignored keeps its reason and expiry, so reopen first to change them. allowed: false reopens them. Another product using the license still raises its finding; to approve it everywhere, use taiga_change_license. Allowing a license is the person's decision. NOT_FOUND when the product has no license finding for it.",
|
|
29073
|
+
inputSchema: object({
|
|
29074
|
+
productId,
|
|
29075
|
+
license: string$2().trim().min(1).max(MAINTENANCE_LICENSE_FILTER_MAX).describe("The license as the product's license findings or taiga_list_packages name it, such as MPL-2.0"),
|
|
29076
|
+
allowed: boolean().describe("true allows the license in this product, false reopens its findings"),
|
|
29077
|
+
reason: _enum(LICENSE_ALLOW_REASONS).optional().describe("Why it is allowed; required when allowing. accepted: reviewed and allowed here, usually for a limited time; not_handed_out: the product runs only internally or on your own servers, its code is never given to anyone; no_alternative: no package under an approved license does the job yet; read_wrong: the packages are not actually under this license"),
|
|
29078
|
+
note: string$2().trim().max(280).optional().describe("A short justification, kept for audit"),
|
|
29079
|
+
expiresAt: string$2().datetime({ offset: true }).meta({ pattern: void 0 }).optional().describe("When the allowance lapses and the findings come back if still there (ISO 8601, in the future)")
|
|
29080
|
+
}),
|
|
29081
|
+
annotations: OPERATE_SETTING
|
|
29082
|
+
}, (params) => runTool(async () => {
|
|
29083
|
+
if (!params.allowed) return toolResult((await client.reopenProductLicense(params.productId, params.license)).data);
|
|
29084
|
+
if (params.reason === void 0) return toolError("VALIDATION_ERROR", "Say why the license is allowed (reason).");
|
|
29085
|
+
return toolResult((await client.allowProductLicense(params.productId, {
|
|
29086
|
+
license: params.license,
|
|
29087
|
+
ignoreReason: LICENSE_ALLOW_REASON_TO_IGNORE[params.reason],
|
|
29088
|
+
...params.note !== void 0 ? { ignoreNote: params.note } : {},
|
|
29089
|
+
...params.expiresAt !== void 0 ? { ignoreExpiresAt: params.expiresAt } : {}
|
|
29090
|
+
})).data);
|
|
29091
|
+
}));
|
|
27991
29092
|
server.registerTool("taiga_fix_finding", {
|
|
27992
29093
|
title: "Fix a finding",
|
|
27993
|
-
description: "Raise an initiative that fixes a finding; it ends in a pull request, which merges on its own only where the product's auto-merge allows it. wholeGroup: true fixes, in that repository, every open advisory on that package version in one bump, or every place the rule flagged. A malicious package is removed, with every advisory on that version, whatever wholeGroup says. Refused when no
|
|
29094
|
+
description: "Raise an initiative that fixes a finding; it ends in a pull request, which merges on its own only where the product's auto-merge allows it. wholeGroup: true fixes, in that repository, every open advisory on that package version in one bump, or every place the rule flagged. A malicious package is removed, with every advisory on that version, whatever wholeGroup says. A license finding the row marks fixable has its packages replaced by ones on the approved list. A language runtime moves every file declaring it together, in a container image only to a tag the image has published. Refused when there is no version to move to (fixable: false; for a runtime, also while its image has not published the fix), the code is in a submodule, or a secret is only in history (rotate it outside Taiga).",
|
|
27994
29095
|
inputSchema: object({
|
|
27995
29096
|
productId,
|
|
27996
29097
|
findingId,
|
|
@@ -28029,6 +29130,13 @@ function registerMaintenanceTools(server, client) {
|
|
|
28029
29130
|
const body = { refuseIfFixedSinceAssessment: params.fixAgain !== true };
|
|
28030
29131
|
return toolResult((params.controlId ? await client.remediatePolicyControl(params.productId, params.controlId, body) : await client.remediatePolicyViolation(params.productId, params.violationId, body)).data);
|
|
28031
29132
|
}));
|
|
29133
|
+
if (!client.hasCapability("backlog:write")) return;
|
|
29134
|
+
server.registerTool("taiga_ship_notices", {
|
|
29135
|
+
title: "Ship open-source notices",
|
|
29136
|
+
description: "Raise the initiative that builds open-source notices into a product's Vite web app: every production build writes the license texts and NOTICE files of the packages in its browser bundle, and the app shows them at /legal/open-source. Taiga writes the brief from the inventory; the plan waits for review before it builds. One that is already open is handed back (created: false) rather than raised again. Refused where no repository of the product declares Vite.",
|
|
29137
|
+
inputSchema: object({ productId }),
|
|
29138
|
+
annotations: OPERATE
|
|
29139
|
+
}, (params) => runTool(async () => toolResult((await client.shipNotices(params.productId)).data)));
|
|
28032
29140
|
}
|
|
28033
29141
|
//#endregion
|
|
28034
29142
|
//#region src/tools/sections.ts
|
|
@@ -28354,20 +29462,17 @@ async function setupReadiness(client, product, links) {
|
|
|
28354
29462
|
const settings = product["settings"] ?? {};
|
|
28355
29463
|
const importState = imported ? IMPORT_STATE[String(settings.importStatus)] ?? "not started" : void 0;
|
|
28356
29464
|
const missing = blockers ? blockers.missingDocuments : null;
|
|
28357
|
-
const openChecks = blockers ? blockers.openChecks.map((check) => check.criterion) : null;
|
|
28358
29465
|
return {
|
|
28359
|
-
ready: blockers ? missing.length === 0
|
|
29466
|
+
ready: blockers ? missing.length === 0 : null,
|
|
28360
29467
|
repository,
|
|
28361
29468
|
...imported ? { import: typeof settings.importStatus === "string" ? settings.importStatus : "pending" } : {},
|
|
28362
29469
|
...imported ? {} : { discoveryStep: full["discoveryStep"] ?? null },
|
|
28363
29470
|
missingDocuments: missing,
|
|
28364
|
-
openChecks,
|
|
28365
29471
|
nextStep: nextStepFor({
|
|
28366
29472
|
imported,
|
|
28367
29473
|
importState,
|
|
28368
29474
|
linked: repository?.linked ?? null,
|
|
28369
29475
|
missing,
|
|
28370
|
-
openChecks,
|
|
28371
29476
|
product: full
|
|
28372
29477
|
})
|
|
28373
29478
|
};
|
|
@@ -28389,7 +29494,6 @@ function nextStepFor(state) {
|
|
|
28389
29494
|
if (!state.missing) return setupNextStep(state.product);
|
|
28390
29495
|
if (state.missing.includes("specification")) return "The specification comes from the Discovery conversation, held in the web: the person describes the product there. The other documents follow from it." + (state.linked === false ? " Linking a repository (taiga_link_repository) can wait until before the first build." : "");
|
|
28391
29496
|
if (state.missing.length > 0) return `Still to publish: ${named(state.missing)}. taiga_generate_product_documents generates the missing ones (paid agent work: ask first); a draft is published with taiga_publish_product_document.`;
|
|
28392
|
-
if (state.openChecks && state.openChecks.length > 0) return `The documents are published; the checks are stamped in the web, then ${finish}`;
|
|
28393
29497
|
return `Every required document is published: ${finish}`;
|
|
28394
29498
|
}
|
|
28395
29499
|
//#endregion
|
|
@@ -29473,7 +30577,8 @@ const READY_BLOCKER_SAID = {
|
|
|
29473
30577
|
bar_lowering: "a fix lowered a check instead of fixing the code",
|
|
29474
30578
|
bar_lowering_unchecked: "Taiga could not check whether a fix lowered a check",
|
|
29475
30579
|
criticals_unresolved: "Taiga's own review left a critical issue unresolved",
|
|
29476
|
-
clean_checkout_failed: "the branch does not install or pass its tests from a clean checkout"
|
|
30580
|
+
clean_checkout_failed: "the branch does not install or pass its tests from a clean checkout",
|
|
30581
|
+
license_not_approved: "a package it adds has a licence that is not on your approved list"
|
|
29477
30582
|
};
|
|
29478
30583
|
/** The first sentence of a run's error, short enough for a row. */
|
|
29479
30584
|
function errorSaid(error) {
|
|
@@ -30231,6 +31336,10 @@ function standing(item) {
|
|
|
30231
31336
|
const life = item["lifecycle"];
|
|
30232
31337
|
const run = life?.latestBuild;
|
|
30233
31338
|
const situation = situationOf(item);
|
|
31339
|
+
const open = (life?.openPullRequests ?? []).flatMap((pull) => typeof pull.repositoryFullName === "string" && typeof pull.prNumber === "number" ? [{
|
|
31340
|
+
repository: pull.repositoryFullName,
|
|
31341
|
+
prNumber: pull.prNumber
|
|
31342
|
+
}] : []);
|
|
30234
31343
|
return {
|
|
30235
31344
|
column: typeof item["state"] === "string" ? item["state"] : null,
|
|
30236
31345
|
turn: typeof life?.turn === "string" ? life.turn : null,
|
|
@@ -30238,7 +31347,8 @@ function standing(item) {
|
|
|
30238
31347
|
latestRun: run && typeof run.id === "string" ? {
|
|
30239
31348
|
id: run.id,
|
|
30240
31349
|
...typeof run.prNumber === "number" ? { prNumber: run.prNumber } : {}
|
|
30241
|
-
} : null
|
|
31350
|
+
} : null,
|
|
31351
|
+
...open.length > 1 ? { pullRequests: open } : {}
|
|
30242
31352
|
};
|
|
30243
31353
|
}
|
|
30244
31354
|
/** The move each call to action is, in the tools' words. */
|
|
@@ -30421,7 +31531,8 @@ const PLAN_SUMMARY_FIELDS = [
|
|
|
30421
31531
|
"backlogItemId",
|
|
30422
31532
|
"version",
|
|
30423
31533
|
"status",
|
|
30424
|
-
"createdAt"
|
|
31534
|
+
"createdAt",
|
|
31535
|
+
"controls"
|
|
30425
31536
|
];
|
|
30426
31537
|
const PLAN_STEP_SUMMARY_FIELDS = [
|
|
30427
31538
|
"id",
|
|
@@ -30568,10 +31679,12 @@ function registerBacklogReads(server, client) {
|
|
|
30568
31679
|
...revised
|
|
30569
31680
|
});
|
|
30570
31681
|
const steps = Array.isArray(plan.steps) ? plan.steps : [];
|
|
31682
|
+
const several = (plan.repositories?.length ?? 0) > 1;
|
|
30571
31683
|
return toolResult({
|
|
30572
31684
|
...pickFields([plan], PLAN_SUMMARY_FIELDS)[0],
|
|
31685
|
+
...several ? { repositories: plan.repositories } : {},
|
|
30573
31686
|
...revised,
|
|
30574
|
-
steps: pickFields(steps, PLAN_STEP_SUMMARY_FIELDS)
|
|
31687
|
+
steps: several ? pickFields(steps, [...PLAN_STEP_SUMMARY_FIELDS, "repositoryName"]) : pickFields(steps, PLAN_STEP_SUMMARY_FIELDS)
|
|
30575
31688
|
});
|
|
30576
31689
|
}));
|
|
30577
31690
|
}
|
|
@@ -31527,7 +32640,7 @@ async function licenseReviews(client, productId, now) {
|
|
|
31527
32640
|
packages: data["packages"],
|
|
31528
32641
|
...Number.isNaN(created) ? {} : { waitingDays: Math.floor((now - created) / DAY_MS) },
|
|
31529
32642
|
readWith: "taiga_list_licenses (license)",
|
|
31530
|
-
decideWith: "taiga_change_license (license, action: 'approve'); dismissing it is only in the web"
|
|
32643
|
+
decideWith: "taiga_change_license (license, action: 'approve') for every product" + (client.hasCapability("products:write") ? ", or taiga_allow_license (productId, license) for this one" : "") + "; dismissing it is only in the web"
|
|
31531
32644
|
};
|
|
31532
32645
|
}).filter((r) => productId === void 0 || r.productId === productId),
|
|
31533
32646
|
truncated: result.truncated === true
|
|
@@ -48294,7 +49407,7 @@ function registerSetupTools(server, client) {
|
|
|
48294
49407
|
}, (params) => runTool(async () => toolResult((await client.refreshRealityDocuments(params.productId, params.docTypes)).data)));
|
|
48295
49408
|
server.registerTool("taiga_finish_discovery", {
|
|
48296
49409
|
title: "Finish Discovery",
|
|
48297
|
-
description: "Finish a product's setup: lock its documents and start planning its first initiatives. Refused while documents are missing (the refusal lists them)
|
|
49410
|
+
description: "Finish a product's setup: lock its documents and start planning its first initiatives. Refused while documents are missing (the refusal lists them).",
|
|
48298
49411
|
inputSchema: object({ productId }),
|
|
48299
49412
|
annotations: OPERATE
|
|
48300
49413
|
}, (params) => runTool(async () => {
|
|
@@ -48338,6 +49451,7 @@ function withStrictInputs(server, client, registered, toolsets, left) {
|
|
|
48338
49451
|
const inputs = /* @__PURE__ */ new Map();
|
|
48339
49452
|
const register = server.registerTool.bind(server);
|
|
48340
49453
|
server.registerTool = ((name, config, callback) => {
|
|
49454
|
+
if (DELIVERY_TOOLS.has(name) && client.getIntrospection()?.deliveryIncluded === false) return void 0;
|
|
48341
49455
|
const toolset = TOOLSET_OF[name];
|
|
48342
49456
|
if (name !== "taiga_whoami") {
|
|
48343
49457
|
if (!toolset) throw new Error(`Tool ${name}: no toolset in toolsets.ts`);
|
|
@@ -48558,6 +49672,7 @@ const PAID_WORK = /* @__PURE__ */ new Set([
|
|
|
48558
49672
|
"taiga_request_changes",
|
|
48559
49673
|
"taiga_request_initiatives",
|
|
48560
49674
|
"taiga_resume_initiative",
|
|
49675
|
+
"taiga_ship_notices",
|
|
48561
49676
|
"taiga_retry_knowledge",
|
|
48562
49677
|
"taiga_review_plan",
|
|
48563
49678
|
"taiga_set_auto_build",
|