@cassiomc1/forgeloop 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/copilot-instructions.md +1 -1
- package/AGENTS.md +1 -1
- package/AGENT_COMPATIBILITY.md +8 -0
- package/CLAUDE.md +1 -1
- package/CONTRIBUTING.md +90 -0
- package/DOCS_INDEX.md +46 -12
- package/ENG/c-development-eng.md +112 -0
- package/ENG/cpp-development-eng.md +109 -0
- package/ENG/dotnet-aspnetcore-development-eng.md +401 -0
- package/ENG/go-development-eng.md +103 -0
- package/ENG/java-development-eng.md +125 -0
- package/ENG/nodejs-backend-development-eng.md +605 -0
- package/ENG/php-development-eng.md +104 -0
- package/ENG/rust-development-eng.md +422 -0
- package/ENG/sec-code-eng.md +7 -7
- package/ENG/sql-development-eng.md +108 -0
- package/ENG/swift-development-eng.md +111 -0
- package/ENG/typescript-development-eng.md +108 -0
- package/EXECUTION_STATE.md +12 -0
- package/GUIDE_ROUTER.md +418 -9
- package/LOOP_ENGINEERING.md +28 -2
- package/ORCHESTRATOR_INTEGRATION.md +9 -5
- package/PROTOCOL_INTEGRATION.md +55 -2
- package/QUALITY_SCORECARD.md +1 -0
- package/README.md +78 -52
- package/TERMINOLOGY.md +2 -0
- package/THIRD_PARTY_NOTICES.md +19 -7
- package/THREAT_MODEL.md +140 -1
- package/completions/_forgeloop +22 -4
- package/completions/forgeloop.bash +40 -4
- package/completions/forgeloop.fish +130 -1
- package/docs/ADVISORY_CONTEXT.md +25 -0
- package/docs/AGENT_BROWSER_ADAPTER.md +81 -0
- package/docs/AGENT_BROWSER_VERIFICATION.md +6 -0
- package/docs/AGENT_PROTOCOL_SUMMARY.md +81 -3
- package/docs/AGENT_SKILL.md +66 -0
- package/docs/ARTIFACT_REFERENCE.md +123 -0
- package/docs/AUDIT_UX.md +46 -0
- package/docs/BROWSER_VERIFICATION.md +136 -0
- package/docs/CLI_REFERENCE.md +392 -10
- package/docs/CODE_ATTESTATION.md +2 -2
- package/docs/DOCUMENTATION_GUIDE.md +34 -12
- package/docs/GETTING_STARTED.md +59 -0
- package/docs/JEV_BENCHMARKS.md +31 -0
- package/docs/MODEL_ROUTING.md +37 -0
- package/docs/OPENSRC_ADAPTER.md +241 -0
- package/docs/PACKAGE_CONTENTS.md +60 -19
- package/docs/PROVIDERS.md +126 -0
- package/docs/PROVIDER_ARCHITECTURE.md +199 -0
- package/docs/RECIPES.md +32 -0
- package/docs/RELEASE_CHECKLIST.md +66 -5
- package/docs/SECURITY_REVIEW.md +71 -0
- package/docs/SEMANTIC_DECISION_PLANE.md +71 -0
- package/docs/TEST_INTELLIGENCE.md +29 -0
- package/docs/TEST_PRUNING.md +14 -0
- package/docs/TROUBLESHOOTING.md +298 -3
- package/docs/UNIVERSAL_INTEGRATION.md +31 -0
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +2 -2
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +1 -1
- package/docs/assets/diagrams/forgeloop-engineering-flow.html +39 -26
- package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
- package/docs/assets/diagrams/forgeloop-engineering-flow.svg +26 -26
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +2 -1
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +1 -1
- package/docs/diagrams/README.md +13 -9
- package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +1 -1
- package/docs/diagrams/forgeloop-engineering-flow.workflow.json +24 -19
- package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +1 -0
- package/docs/diagrams/reviews/forgeloop-code-attestation-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json +4 -4
- package/docs/documentation-manifest.json +1397 -0
- package/docs/protocol-requirements.json +101 -0
- package/package.json +46 -4
- package/schemas/config.schema.json +14 -0
- package/schemas/context-plan.schema.json +18 -0
- package/schemas/routing-input.schema.json +1 -1
- package/schemas/semantic-decision.schema.json +46 -0
- package/schemas/test-utility.schema.json +44 -0
- package/scripts/CI_VALIDATORS.md +84 -11
- package/scripts/benchmark-jev.mjs +5 -0
- package/scripts/benchmark-test-intelligence.mjs +4 -0
- package/scripts/generate-agent-protocol-summary.mjs +40 -1
- package/scripts/generate-forgeloop-skill.mjs +133 -0
- package/scripts/jev-smoke.mjs +19 -0
- package/skills/forgeloop/README.md +9 -0
- package/skills/forgeloop/SKILL.md +77 -0
- package/skills/forgeloop/references/lifecycle.md +9 -0
- package/skills/forgeloop/references/recovery.md +7 -0
- package/skills/forgeloop/references/verification.md +7 -0
- package/src/adapters/agent-browser/assertions.js +47 -0
- package/src/adapters/agent-browser/commands.js +54 -0
- package/src/adapters/agent-browser/index.js +3 -0
- package/src/adapters/agent-browser/locator.js +40 -0
- package/src/adapters/agent-browser/process.js +215 -0
- package/src/adapters/agent-browser/provider.js +313 -0
- package/src/adapters/emulated-services/constants.js +24 -0
- package/src/adapters/emulated-services/index.js +7 -0
- package/src/adapters/emulated-services/process.js +162 -0
- package/src/adapters/emulated-services/provider.js +282 -0
- package/src/adapters/opensrc/normalize.js +90 -0
- package/src/adapters/opensrc/process.js +248 -0
- package/src/adapters/opensrc/provider.js +338 -0
- package/src/adapters/opensrc/search.js +264 -0
- package/src/adapters/typesafe/client.js +28 -0
- package/src/adapters/typesafe/engine.js +63 -0
- package/src/adapters/typesafe/normalize.js +41 -0
- package/src/cli.js +108 -0
- package/src/commands/checkpoint-revalidate.js +176 -0
- package/src/commands/context-plan.js +38 -0
- package/src/commands/contract-create.js +264 -0
- package/src/commands/contract-revise.js +236 -0
- package/src/commands/decision-show.js +14 -0
- package/src/commands/decision-status.js +22 -0
- package/src/commands/discover.js +41 -0
- package/src/commands/doctor.js +15 -0
- package/src/commands/gate-record.js +205 -0
- package/src/commands/gate-revalidate.js +137 -0
- package/src/commands/model-route.js +32 -0
- package/src/commands/next.js +19 -7
- package/src/commands/route.js +146 -18
- package/src/commands/semantic-plan.js +17 -0
- package/src/commands/task-abandon.js +224 -0
- package/src/commands/task-create.js +84 -25
- package/src/commands/task-list.js +22 -2
- package/src/commands/task-migrate-contract-bootstrap-repair.js +288 -0
- package/src/commands/task-repair-contract-bootstrap.js +263 -0
- package/src/commands/test-inventory.js +5 -0
- package/src/commands/test-prune-plan.js +5 -0
- package/src/commands/test-prune-probe.js +5 -0
- package/src/commands/test-utility.js +5 -0
- package/src/commands/validate-protocol.js +10 -1
- package/src/config/guides.json +44 -0
- package/src/core/artifact-registry.js +24 -0
- package/src/core/audit-ux.js +514 -0
- package/src/core/browser-verification/constants.js +149 -0
- package/src/core/browser-verification/normalize.js +254 -0
- package/src/core/browser-verification/provider.js +519 -0
- package/src/core/browser-verification/service.js +115 -0
- package/src/core/build-script.js +151 -0
- package/src/core/c-cpp-project.js +143 -0
- package/src/core/checkpoint-revalidation.js +319 -0
- package/src/core/cli-command-definitions.js +249 -1
- package/src/core/command-executors.js +115 -3
- package/src/core/command-input.js +212 -102
- package/src/core/completion-artifacts.js +14 -5
- package/src/core/completion.js +4 -6
- package/src/core/config.js +3 -0
- package/src/core/context-compiler/budget.js +9 -0
- package/src/core/context-compiler/candidates.js +39 -0
- package/src/core/context-compiler/compiler.js +63 -0
- package/src/core/context-compiler/fingerprint.js +11 -0
- package/src/core/context-compiler/policy.js +13 -0
- package/src/core/context-compiler/result.js +23 -0
- package/src/core/contract-bootstrap-recovery.js +655 -0
- package/src/core/contract-presets.js +82 -0
- package/src/core/contract-revision.js +210 -0
- package/src/core/decision/artifact.js +69 -0
- package/src/core/decision/benchmarks.js +103 -0
- package/src/core/decision/cache.js +27 -0
- package/src/core/decision/constants.js +58 -0
- package/src/core/decision/cutover.js +34 -0
- package/src/core/decision/engine.js +22 -0
- package/src/core/decision/errors.js +68 -0
- package/src/core/decision/events.js +101 -0
- package/src/core/decision/freshness.js +19 -0
- package/src/core/decision/normalizers/index.js +115 -0
- package/src/core/decision/policy.js +18 -0
- package/src/core/decision/projection.js +16 -0
- package/src/core/decision/question-registry.js +201 -0
- package/src/core/decision/request.js +26 -0
- package/src/core/decision/resolver.js +130 -0
- package/src/core/decision/result.js +58 -0
- package/src/core/decision/service.js +156 -0
- package/src/core/decision/state-builder.js +65 -0
- package/src/core/decision/task-bindings.js +30 -0
- package/src/core/decision/test-provider.js +32 -0
- package/src/core/decision/thresholds.js +15 -0
- package/src/core/error-codes.js +281 -3
- package/src/core/events.js +226 -57
- package/src/core/evidence-readiness.js +9 -0
- package/src/core/execution-prerequisites.js +14 -0
- package/src/core/execution-profile.js +63 -38
- package/src/core/filesystem.js +1 -10
- package/src/core/gate-provenance.js +124 -0
- package/src/core/go-project.js +206 -0
- package/src/core/integration-invocation-policy.js +27 -4
- package/src/core/integration-resources.js +86 -61
- package/src/core/java-project.js +403 -0
- package/src/core/model-router/constants.js +10 -0
- package/src/core/model-router/policy.js +103 -0
- package/src/core/model-router/router.js +37 -0
- package/src/core/multi-language-project.js +117 -0
- package/src/core/next-action-model.js +58 -0
- package/src/core/next-action-phases.js +130 -42
- package/src/core/next-action-refresh.js +43 -9
- package/src/core/next-action-review-phase.js +7 -2
- package/src/core/next-action.js +35 -7
- package/src/core/next-explanation.js +63 -0
- package/src/core/phase.js +128 -10
- package/src/core/php-project.js +85 -0
- package/src/core/preflight-consistency.js +23 -9
- package/src/core/preflight-loaders.js +37 -5
- package/src/core/project-detection.js +1760 -52
- package/src/core/protocol-info.js +65 -0
- package/src/core/protocol.js +20 -0
- package/src/core/reconcile-closure.js +132 -53
- package/src/core/recovery-history.js +1 -0
- package/src/core/resumability.js +154 -44
- package/src/core/route-artifact.js +15 -1
- package/src/core/router.js +223 -4
- package/src/core/runtime-context.js +118 -61
- package/src/core/rust-project.js +400 -0
- package/src/core/schema-validation.js +3 -0
- package/src/core/security-review/constants.js +64 -0
- package/src/core/security-review/normalize.js +245 -0
- package/src/core/security-review/provider.js +204 -0
- package/src/core/security-review/service.js +134 -0
- package/src/core/semantic-planning/constants.js +19 -0
- package/src/core/semantic-planning/projection.js +94 -0
- package/src/core/semantic-planning/service.js +15 -0
- package/src/core/sources.js +37 -0
- package/src/core/sql-project.js +141 -0
- package/src/core/swift-project.js +200 -0
- package/src/core/task-claim-state.js +201 -1
- package/src/core/task-conflict-inspection.js +31 -5
- package/src/core/task-paths.js +13 -0
- package/src/core/task-recovery.js +1 -0
- package/src/core/templates.js +3 -0
- package/src/core/test-intelligence/benchmarks.js +68 -0
- package/src/core/test-intelligence/inventory.js +73 -0
- package/src/core/test-intelligence/prune.js +90 -0
- package/src/core/test-intelligence/semantic-state.js +15 -0
- package/src/core/test-intelligence/service.js +40 -0
- package/src/core/test-intelligence/utility.js +50 -0
- package/src/core/trace.js +11 -7
- package/src/core/transaction.js +1 -0
- package/src/core/typescript-project.js +349 -0
- package/src/core/xml-structure.js +123 -0
- package/src/integration.d.ts +492 -0
- package/src/integration.js +54 -0
- package/src/providers/README.md +47 -0
- package/src/providers/capabilities.js +46 -0
- package/src/providers/errors.js +15 -0
- package/src/providers/index.js +29 -0
- package/src/providers/json-snapshot.js +105 -0
- package/src/providers/registry.js +152 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import { parse as parseToml } from "smol-toml";
|
|
4
|
+
|
|
5
|
+
export const CARGO_MANIFEST_MAX_BYTES = 1024 * 1024;
|
|
6
|
+
|
|
7
|
+
export const RUST_SHARED_FILES = Object.freeze([
|
|
8
|
+
"Cargo.lock",
|
|
9
|
+
"rust-toolchain.toml",
|
|
10
|
+
"rust-toolchain",
|
|
11
|
+
"rustfmt.toml",
|
|
12
|
+
".rustfmt.toml",
|
|
13
|
+
"clippy.toml",
|
|
14
|
+
".clippy.toml",
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
const RUST_BACKEND_CONTEXTS = new Set([
|
|
18
|
+
"actix-web",
|
|
19
|
+
"axum",
|
|
20
|
+
"hyper",
|
|
21
|
+
"poem",
|
|
22
|
+
"rocket",
|
|
23
|
+
"tokio",
|
|
24
|
+
"tonic",
|
|
25
|
+
"tower",
|
|
26
|
+
"warp",
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
function isPlainObject(value) {
|
|
30
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function uniqueSorted(values) {
|
|
34
|
+
return [...new Set(values.filter((value) => typeof value === "string" && value.length > 0))]
|
|
35
|
+
.sort((left, right) => left.localeCompare(right));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function invalidCargoManifest() {
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
rust: false,
|
|
42
|
+
package: { present: false, name: null, edition: null, rustVersion: null },
|
|
43
|
+
workspace: { present: false, members: [], exclude: [], defaultMembers: [], resolver: null },
|
|
44
|
+
dependencies: [],
|
|
45
|
+
workspaceDependencies: [],
|
|
46
|
+
devDependencies: [],
|
|
47
|
+
buildDependencies: [],
|
|
48
|
+
targetDependencies: [],
|
|
49
|
+
pathDependencies: [],
|
|
50
|
+
workspaceDependencyPaths: [],
|
|
51
|
+
workspaceDependencyUses: [],
|
|
52
|
+
features: [],
|
|
53
|
+
backendContexts: [],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function optionalString(object, key) {
|
|
58
|
+
if (object[key] === undefined) return null;
|
|
59
|
+
return typeof object[key] === "string" && object[key].trim() !== "" ? object[key] : undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function cargoPackageString(object, key) {
|
|
63
|
+
if (object[key] === undefined) return { value: null, inherited: false };
|
|
64
|
+
if (typeof object[key] === "string" && object[key].trim() !== "") {
|
|
65
|
+
return { value: object[key], inherited: false };
|
|
66
|
+
}
|
|
67
|
+
if (isPlainObject(object[key])
|
|
68
|
+
&& object[key].workspace === true
|
|
69
|
+
&& Object.keys(object[key]).every((property) => property === "workspace")) {
|
|
70
|
+
return { value: null, inherited: true };
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function optionalStringArray(object, key) {
|
|
76
|
+
if (object[key] === undefined) return [];
|
|
77
|
+
if (!Array.isArray(object[key]) || object[key].some((value) => typeof value !== "string" || value.trim() === "")) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return [...object[key]];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function dependencyNamesFromTable(value) {
|
|
84
|
+
if (value === undefined) return [];
|
|
85
|
+
if (!isPlainObject(value)) return null;
|
|
86
|
+
return Object.keys(value);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function pathDependenciesFromTable(value) {
|
|
90
|
+
if (value === undefined) return [];
|
|
91
|
+
if (!isPlainObject(value)) return null;
|
|
92
|
+
return Object.entries(value)
|
|
93
|
+
.filter(([, specification]) => isPlainObject(specification)
|
|
94
|
+
&& specification.workspace !== true
|
|
95
|
+
&& typeof specification.path === "string"
|
|
96
|
+
&& specification.path.trim() !== "")
|
|
97
|
+
.map(([name, specification]) => ({ name, path: specification.path }));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function workspaceDependencyUsesFromTable(value) {
|
|
101
|
+
if (value === undefined) return [];
|
|
102
|
+
if (!isPlainObject(value)) return null;
|
|
103
|
+
return Object.entries(value)
|
|
104
|
+
.filter(([, specification]) => isPlainObject(specification) && specification.workspace === true)
|
|
105
|
+
.map(([name]) => name);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function dependencyMetadataFromTable(value) {
|
|
109
|
+
const names = dependencyNamesFromTable(value);
|
|
110
|
+
const pathDependencies = pathDependenciesFromTable(value);
|
|
111
|
+
const workspaceDependencyUses = workspaceDependencyUsesFromTable(value);
|
|
112
|
+
if (names === null || pathDependencies === null || workspaceDependencyUses === null) return null;
|
|
113
|
+
return { names, pathDependencies, workspaceDependencyUses };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function uniquePathDependencies(values) {
|
|
117
|
+
const entries = new Map();
|
|
118
|
+
for (const dependency of values) {
|
|
119
|
+
if (!dependency || typeof dependency.name !== "string" || typeof dependency.path !== "string") continue;
|
|
120
|
+
const name = dependency.name.trim();
|
|
121
|
+
const dependencyPath = dependency.path.trim();
|
|
122
|
+
if (name === "" || dependencyPath === "") continue;
|
|
123
|
+
entries.set(`${name}\u0000${dependencyPath}`, { name, path: dependencyPath });
|
|
124
|
+
}
|
|
125
|
+
return [...entries.values()].sort((left, right) => {
|
|
126
|
+
const byName = left.name.localeCompare(right.name);
|
|
127
|
+
return byName || left.path.localeCompare(right.path);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function dependencyMetadataFromTargets(value) {
|
|
132
|
+
if (value === undefined) return { names: [], pathDependencies: [], workspaceDependencyUses: [] };
|
|
133
|
+
if (!isPlainObject(value)) return null;
|
|
134
|
+
const names = [];
|
|
135
|
+
const pathDependencies = [];
|
|
136
|
+
const workspaceDependencyUses = [];
|
|
137
|
+
for (const target of Object.values(value)) {
|
|
138
|
+
if (!isPlainObject(target)) return null;
|
|
139
|
+
for (const section of ["dependencies", "dev-dependencies", "build-dependencies"]) {
|
|
140
|
+
const dependencies = dependencyMetadataFromTable(target[section]);
|
|
141
|
+
if (dependencies === null) return null;
|
|
142
|
+
names.push(...dependencies.names);
|
|
143
|
+
pathDependencies.push(...dependencies.pathDependencies);
|
|
144
|
+
workspaceDependencyUses.push(...dependencies.workspaceDependencyUses);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { names: uniqueSorted(names), pathDependencies, workspaceDependencyUses };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function parseCargoSections(document) {
|
|
151
|
+
const dependencies = dependencyMetadataFromTable(document.dependencies);
|
|
152
|
+
const workspaceDependencies = dependencyMetadataFromTable(document.workspace?.dependencies);
|
|
153
|
+
const devDependencies = dependencyMetadataFromTable(document["dev-dependencies"]);
|
|
154
|
+
const buildDependencies = dependencyMetadataFromTable(document["build-dependencies"]);
|
|
155
|
+
const targetDependencies = dependencyMetadataFromTargets(document.target);
|
|
156
|
+
const features = document.features === undefined
|
|
157
|
+
? []
|
|
158
|
+
: isPlainObject(document.features) ? Object.keys(document.features) : null;
|
|
159
|
+
if ([dependencies, workspaceDependencies, devDependencies, buildDependencies, targetDependencies, features]
|
|
160
|
+
.some((value) => value === null)) return null;
|
|
161
|
+
return {
|
|
162
|
+
dependencies: uniqueSorted(dependencies.names),
|
|
163
|
+
workspaceDependencies: uniqueSorted(workspaceDependencies.names),
|
|
164
|
+
devDependencies: uniqueSorted(devDependencies.names),
|
|
165
|
+
buildDependencies: uniqueSorted(buildDependencies.names),
|
|
166
|
+
targetDependencies: targetDependencies.names,
|
|
167
|
+
pathDependencies: uniquePathDependencies([
|
|
168
|
+
...dependencies.pathDependencies,
|
|
169
|
+
...devDependencies.pathDependencies,
|
|
170
|
+
...buildDependencies.pathDependencies,
|
|
171
|
+
...targetDependencies.pathDependencies,
|
|
172
|
+
]),
|
|
173
|
+
workspaceDependencyPaths: uniquePathDependencies(workspaceDependencies.pathDependencies),
|
|
174
|
+
workspaceDependencyUses: uniqueSorted([
|
|
175
|
+
...dependencies.workspaceDependencyUses,
|
|
176
|
+
...devDependencies.workspaceDependencyUses,
|
|
177
|
+
...buildDependencies.workspaceDependencyUses,
|
|
178
|
+
...targetDependencies.workspaceDependencyUses,
|
|
179
|
+
]),
|
|
180
|
+
features: uniqueSorted(features),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function parseCargoPackage(document) {
|
|
185
|
+
if (document.package === undefined) {
|
|
186
|
+
return { present: false, name: null, edition: null, rustVersion: null };
|
|
187
|
+
}
|
|
188
|
+
if (!isPlainObject(document.package)) return null;
|
|
189
|
+
const name = optionalString(document.package, "name");
|
|
190
|
+
const edition = cargoPackageString(document.package, "edition");
|
|
191
|
+
const rustVersion = cargoPackageString(document.package, "rust-version");
|
|
192
|
+
const workspace = optionalString(document.package, "workspace");
|
|
193
|
+
if (name === undefined || name === null || edition === null || rustVersion === null || workspace === undefined) return null;
|
|
194
|
+
const metadata = {
|
|
195
|
+
present: true,
|
|
196
|
+
name,
|
|
197
|
+
edition: edition.value,
|
|
198
|
+
rustVersion: rustVersion.value,
|
|
199
|
+
};
|
|
200
|
+
if (edition.inherited) metadata.editionInherited = true;
|
|
201
|
+
if (rustVersion.inherited) metadata.rustVersionInherited = true;
|
|
202
|
+
if (workspace !== null) metadata.workspace = workspace;
|
|
203
|
+
return metadata;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function parseCargoWorkspacePackage(workspace) {
|
|
207
|
+
if (workspace.package === undefined) return null;
|
|
208
|
+
if (!isPlainObject(workspace.package)) return undefined;
|
|
209
|
+
const edition = optionalString(workspace.package, "edition");
|
|
210
|
+
const rustVersion = optionalString(workspace.package, "rust-version");
|
|
211
|
+
if (edition === undefined || rustVersion === undefined) return undefined;
|
|
212
|
+
return { present: true, edition, rustVersion };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function parseCargoWorkspace(document) {
|
|
216
|
+
if (document.workspace === undefined) {
|
|
217
|
+
return { present: false, members: [], exclude: [], defaultMembers: [], resolver: null };
|
|
218
|
+
}
|
|
219
|
+
if (!isPlainObject(document.workspace)) return null;
|
|
220
|
+
const members = optionalStringArray(document.workspace, "members");
|
|
221
|
+
const exclude = optionalStringArray(document.workspace, "exclude");
|
|
222
|
+
const defaultMembers = optionalStringArray(document.workspace, "default-members");
|
|
223
|
+
const resolver = optionalString(document.workspace, "resolver");
|
|
224
|
+
if (members === null || exclude === null || defaultMembers === null || resolver === undefined) return null;
|
|
225
|
+
if (document.workspace.dependencies !== undefined && !isPlainObject(document.workspace.dependencies)) return null;
|
|
226
|
+
const workspacePackage = parseCargoWorkspacePackage(document.workspace);
|
|
227
|
+
if (workspacePackage === undefined) return null;
|
|
228
|
+
const metadata = { present: true, members, exclude, defaultMembers, resolver };
|
|
229
|
+
if (workspacePackage) metadata.package = workspacePackage;
|
|
230
|
+
return metadata;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function parseCargoManifest(text) {
|
|
234
|
+
const invalid = invalidCargoManifest();
|
|
235
|
+
if (typeof text !== "string" || text.length > CARGO_MANIFEST_MAX_BYTES || /\r(?!\n)/u.test(text)) return invalid;
|
|
236
|
+
|
|
237
|
+
let document;
|
|
238
|
+
try {
|
|
239
|
+
document = parseToml(text);
|
|
240
|
+
} catch {
|
|
241
|
+
return invalid;
|
|
242
|
+
}
|
|
243
|
+
if (!isPlainObject(document)) return invalid;
|
|
244
|
+
|
|
245
|
+
const packageMetadata = parseCargoPackage(document);
|
|
246
|
+
const workspaceMetadata = parseCargoWorkspace(document);
|
|
247
|
+
const sections = parseCargoSections(document);
|
|
248
|
+
if (packageMetadata?.present
|
|
249
|
+
&& Object.prototype.hasOwnProperty.call(packageMetadata, "workspace")
|
|
250
|
+
&& workspaceMetadata?.present) return invalid;
|
|
251
|
+
if (!packageMetadata || !workspaceMetadata || !sections
|
|
252
|
+
|| (!packageMetadata.present && !workspaceMetadata.present)) return invalid;
|
|
253
|
+
|
|
254
|
+
const allDependencies = [
|
|
255
|
+
...sections.dependencies,
|
|
256
|
+
...sections.workspaceDependencies,
|
|
257
|
+
...sections.devDependencies,
|
|
258
|
+
...sections.buildDependencies,
|
|
259
|
+
...sections.targetDependencies,
|
|
260
|
+
];
|
|
261
|
+
return {
|
|
262
|
+
valid: true,
|
|
263
|
+
rust: true,
|
|
264
|
+
package: packageMetadata,
|
|
265
|
+
workspace: workspaceMetadata,
|
|
266
|
+
...sections,
|
|
267
|
+
backendContexts: uniqueSorted(allDependencies.filter((name) => RUST_BACKEND_CONTEXTS.has(name))),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function normalizedRelativePath(root, candidate) {
|
|
272
|
+
if (typeof root !== "string" || typeof candidate !== "string") return null;
|
|
273
|
+
if (root === candidate) return ".";
|
|
274
|
+
if (root === ".") return candidate.startsWith("./") ? candidate.slice(2) : candidate;
|
|
275
|
+
if (!candidate.startsWith(`${root}/`)) return null;
|
|
276
|
+
return candidate.slice(root.length + 1);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function normalizedCargoPath(root, relativePath) {
|
|
280
|
+
if (typeof root !== "string" || typeof relativePath !== "string" || relativePath.trim() === "") return null;
|
|
281
|
+
const portable = relativePath.trim().replaceAll("\\", "/");
|
|
282
|
+
if (portable.startsWith("/") || /^[A-Za-z]:\//u.test(portable)) return null;
|
|
283
|
+
const joined = path.posix.normalize(path.posix.join(root === "." ? "" : root, portable));
|
|
284
|
+
if (joined === ".." || joined.startsWith("../")) return null;
|
|
285
|
+
return joined.replace(/^\.\//u, "") || ".";
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function normalizedCargoPattern(pattern) {
|
|
289
|
+
if (typeof pattern !== "string" || pattern.trim() === "") return null;
|
|
290
|
+
const portable = pattern.trim().replaceAll("\\", "/");
|
|
291
|
+
if (portable.startsWith("/") || /^[A-Za-z]:\//u.test(portable)) return null;
|
|
292
|
+
const normalized = path.posix.normalize(portable).replace(/^\.\//u, "");
|
|
293
|
+
if (normalized === ".." || normalized.startsWith("../")) return null;
|
|
294
|
+
return normalized;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function cargoPatternRegex(pattern) {
|
|
298
|
+
const normalized = normalizedCargoPattern(pattern);
|
|
299
|
+
if (!normalized) return null;
|
|
300
|
+
let expression = "^";
|
|
301
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
302
|
+
const character = normalized[index];
|
|
303
|
+
if (character === "*" && normalized[index + 1] === "*") {
|
|
304
|
+
expression += ".*";
|
|
305
|
+
index += 1;
|
|
306
|
+
} else if (character === "*") {
|
|
307
|
+
expression += "[^/]*";
|
|
308
|
+
} else if (character === "?") {
|
|
309
|
+
expression += "[^/]";
|
|
310
|
+
} else {
|
|
311
|
+
expression += character.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return new RegExp(`${expression}$`, "u");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function cargoPatternMatches(patterns, relativePath) {
|
|
318
|
+
return patterns.some((pattern) => cargoPatternRegex(pattern)?.test(relativePath));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function isDescendant(root, candidate) {
|
|
322
|
+
return root !== candidate && candidate.startsWith(`${root}/`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function hasNestedWorkspaceBetween(workspaceProject, candidateProject, projects) {
|
|
326
|
+
return projects.some((project) => project !== workspaceProject
|
|
327
|
+
&& project !== candidateProject
|
|
328
|
+
&& project.kind === "rust"
|
|
329
|
+
&& project.rust
|
|
330
|
+
&& project.workspaceRoot
|
|
331
|
+
&& isDescendant(workspaceProject.root, project.root)
|
|
332
|
+
&& (project.root === candidateProject.root || isDescendant(project.root, candidateProject.root)));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function cargoPackageWorkspaceMatches(workspaceProject, candidateProject) {
|
|
336
|
+
const association = candidateProject.internal?.package?.workspace;
|
|
337
|
+
return typeof association === "string"
|
|
338
|
+
&& normalizedCargoPath(candidateProject.root, association) === workspaceProject.root;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function cargoPackageHasExplicitWorkspace(candidateProject) {
|
|
342
|
+
return typeof candidateProject.internal?.package?.workspace === "string";
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function cargoWorkspaceDependencyMatches(workspaceProject, sourceProject, candidateProject) {
|
|
346
|
+
const definitions = workspaceProject.internal?.workspaceDependencyPaths ?? [];
|
|
347
|
+
const uses = sourceProject.internal?.workspaceDependencyUses ?? [];
|
|
348
|
+
return uses.some((name) => definitions.some((dependency) => dependency.name === name
|
|
349
|
+
&& normalizedCargoPath(workspaceProject.root, dependency.path) === candidateProject.root));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function cargoPathDependencyMatches(sourceProject, candidateProject, workspaceProject) {
|
|
353
|
+
const packagePathMatch = (sourceProject.internal?.pathDependencies ?? [])
|
|
354
|
+
.some((dependency) => normalizedCargoPath(sourceProject.root, dependency.path) === candidateProject.root);
|
|
355
|
+
return packagePathMatch || cargoWorkspaceDependencyMatches(workspaceProject, sourceProject, candidateProject);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function cargoWorkspaceContainsInternal(workspaceProject, candidateProject, projects, resolving) {
|
|
359
|
+
if (!workspaceProject?.rust || !workspaceProject.workspaceRoot
|
|
360
|
+
|| !candidateProject?.rust || !candidateProject.packageRoot) return false;
|
|
361
|
+
if (workspaceProject.root === candidateProject.root) return true;
|
|
362
|
+
if (candidateProject.workspaceRoot) return false;
|
|
363
|
+
if (cargoPackageHasExplicitWorkspace(candidateProject)) return cargoPackageWorkspaceMatches(workspaceProject, candidateProject);
|
|
364
|
+
const relative = normalizedRelativePath(workspaceProject.root, candidateProject.root);
|
|
365
|
+
if (!relative || hasNestedWorkspaceBetween(workspaceProject, candidateProject, projects)) return false;
|
|
366
|
+
const workspace = workspaceProject.internal.workspace;
|
|
367
|
+
if (workspace.exclude.some((pattern) => cargoPatternMatches([pattern], relative))) return false;
|
|
368
|
+
if (cargoPatternMatches(workspace.members, relative)) return true;
|
|
369
|
+
if (resolving.has(candidateProject.root)) return false;
|
|
370
|
+
resolving.add(candidateProject.root);
|
|
371
|
+
const contained = projects.some((project) => project !== candidateProject
|
|
372
|
+
&& cargoWorkspaceContainsInternal(workspaceProject, project, projects, resolving)
|
|
373
|
+
&& cargoPathDependencyMatches(project, candidateProject, workspaceProject));
|
|
374
|
+
resolving.delete(candidateProject.root);
|
|
375
|
+
return contained;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function cargoWorkspaceContains(workspaceProject, candidateProject, projects = []) {
|
|
379
|
+
return cargoWorkspaceContainsInternal(workspaceProject, candidateProject, projects, new Set());
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export function isRustSharedFile(relativePath) {
|
|
383
|
+
const normalized = relativePath.replaceAll("\\", "/");
|
|
384
|
+
const fileName = path.posix.basename(normalized).toLowerCase();
|
|
385
|
+
if (RUST_SHARED_FILES.some((name) => name.toLowerCase() === fileName)) return true;
|
|
386
|
+
const parent = path.posix.basename(path.posix.dirname(normalized)).toLowerCase();
|
|
387
|
+
return parent === ".cargo" && ["config", "config.toml"].includes(fileName);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export function rustSharedScopeDirectory(relativePath) {
|
|
391
|
+
const normalized = relativePath.replaceAll("\\", "/");
|
|
392
|
+
const parent = path.posix.dirname(normalized);
|
|
393
|
+
if (path.posix.basename(parent).toLowerCase() === ".cargo") return path.posix.dirname(parent) || ".";
|
|
394
|
+
return parent || ".";
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function rustSharedFileKind(relativePath) {
|
|
398
|
+
if (!isRustSharedFile(relativePath)) return null;
|
|
399
|
+
return path.posix.basename(relativePath).toLowerCase() === "cargo.lock" ? "lockfile" : "configuration";
|
|
400
|
+
}
|
|
@@ -55,6 +55,9 @@ export const SHIPPED_SCHEMA_NAMES = Object.freeze([
|
|
|
55
55
|
"execution-profile-benchmark-run",
|
|
56
56
|
"execution-profile-benchmark-aggregate",
|
|
57
57
|
"structural-quality",
|
|
58
|
+
"semantic-decision",
|
|
59
|
+
"context-plan",
|
|
60
|
+
"test-utility",
|
|
58
61
|
]);
|
|
59
62
|
|
|
60
63
|
export class SchemaValidationError extends Error {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { E_SECURITY_REVIEW_REQUEST_INVALID } from "../error-codes.js";
|
|
2
|
+
|
|
3
|
+
export const SECURITY_REVIEW_LIMITS = Object.freeze({
|
|
4
|
+
maxProjectPathChars: 4096,
|
|
5
|
+
maxTaskIdChars: 128,
|
|
6
|
+
maxReviewIdChars: 128,
|
|
7
|
+
maxRequirementChars: 2000,
|
|
8
|
+
maxRequirements: 64,
|
|
9
|
+
maxPaths: 128,
|
|
10
|
+
maxPathChars: 512,
|
|
11
|
+
maxCategories: 32,
|
|
12
|
+
maxCategoryChars: 64,
|
|
13
|
+
maxRevisionChars: 256,
|
|
14
|
+
maxFindings: 256,
|
|
15
|
+
maxFindingIdChars: 128,
|
|
16
|
+
maxSeverityChars: 16,
|
|
17
|
+
maxTitleChars: 512,
|
|
18
|
+
maxSummaryChars: 4096,
|
|
19
|
+
maxRuleIdChars: 128,
|
|
20
|
+
maxConfidenceChars: 16,
|
|
21
|
+
maxDiagnosticChars: 4000,
|
|
22
|
+
maxDiagnostics: 64,
|
|
23
|
+
maxResultChars: 524288,
|
|
24
|
+
maxSnapshotDepth: 32,
|
|
25
|
+
maxSnapshotNodes: 4096,
|
|
26
|
+
maxSnapshotChars: 524288,
|
|
27
|
+
maxDurationMs: 86_400_000,
|
|
28
|
+
defaultTimeoutMs: 30_000,
|
|
29
|
+
maxTimeoutMs: 120_000,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const SECURITY_REVIEW_SCOPES = Object.freeze(["FULL", "CHANGED", "SELECTED"]);
|
|
33
|
+
export const SECURITY_REVIEW_SEVERITIES = Object.freeze(["INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"]);
|
|
34
|
+
export const SECURITY_REVIEW_CONFIDENCE = Object.freeze(["LOW", "MEDIUM", "HIGH"]);
|
|
35
|
+
export const SECURITY_REVIEW_CATEGORIES = Object.freeze([
|
|
36
|
+
"DEPENDENCY", "SECRETS", "INJECTION", "AUTHENTICATION", "AUTHORIZATION",
|
|
37
|
+
"CRYPTOGRAPHY", "CONFIGURATION", "NETWORK", "FILESYSTEM", "SUPPLY_CHAIN",
|
|
38
|
+
"UNSAFE_EXECUTION", "OTHER",
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
export const SECURITY_REVIEW_TRUST = Object.freeze({
|
|
42
|
+
authority: "OBSERVATION",
|
|
43
|
+
evidenceAuthority: "NONE",
|
|
44
|
+
actionability: "NON_EXECUTABLE",
|
|
45
|
+
trustRole: "NON_EVIDENCE_SECURITY_REVIEW",
|
|
46
|
+
persisted: false,
|
|
47
|
+
lifecycleAuthority: false,
|
|
48
|
+
completionAuthority: false,
|
|
49
|
+
evidenceRequiresForgeLoopValidation: true,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export const SECURITY_REVIEW_BLOCKED_RESULT_FIELDS = Object.freeze([
|
|
53
|
+
"complete", "completed", "nextAction", "releaseClaims", "mutationAllowed", "taskPhase",
|
|
54
|
+
"lifecycleState", "receipt", "evidence", "evidenceStatus", "check", "gate", "satisfied",
|
|
55
|
+
"ownership", "writeClaims", "event", "events", "transaction", "installation", "command",
|
|
56
|
+
"commandArgv", "executable", "shell", "environment", "credentials", "secrets",
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
export function securityReviewRequestError(message, details = null) {
|
|
60
|
+
const error = new Error(message);
|
|
61
|
+
error.code = E_SECURITY_REVIEW_REQUEST_INVALID;
|
|
62
|
+
error.details = details;
|
|
63
|
+
return error;
|
|
64
|
+
}
|