@shiftleftpt/sbd-toe-mcp 0.20.0-beta.4 → 0.20.0-beta.6
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/assets/agent-guide.md +33 -0
- package/consumed-bundle.json +8 -8
- package/data/entities/proportionality.json +2 -2
- package/data/entities/sdlc_integration.json +2 -2
- package/data/publish/indexes/canonical_chunks.jsonl +4903 -4903
- package/data/publish/indexes/publication_manifest.json +4 -4
- package/data/publish/ontology/sbdtoe-ontology.yaml +1699 -21
- package/data/publish/overlay/framework_overlay_index.json +121 -11
- package/data/publish/overlay/overlay_mappings.jsonl +935 -646
- package/data/publish/runtime/artifact_requirements.json +218 -311
- package/data/publish/runtime/artifacts.json +82 -55
- package/data/publish/runtime/controls.json +870 -311
- package/data/publish/runtime/deterministic_manifest.json +2 -2
- package/data/publish/runtime/evidence_patterns.json +162 -162
- package/data/publish/runtime/requirement_control_links.json +2271 -1708
- package/data/publish/runtime/v1/manual_threat_mitigation.jsonl +233 -233
- package/data/publish/runtime/v1/v1_manifest.json +6 -4
- package/data/reports/run_manifest.json +4 -4
- package/dist/index.js +66 -3
- package/dist/index.js.map +1 -1
- package/dist/serving/affordances.d.ts +1 -0
- package/dist/serving/affordances.js +12 -3
- package/dist/serving/affordances.js.map +1 -1
- package/dist/serving/selection.d.ts +56 -0
- package/dist/serving/selection.js +289 -0
- package/dist/serving/selection.js.map +1 -0
- package/dist/tools/consult-security-requirements.d.ts +7 -0
- package/dist/tools/consult-security-requirements.js +38 -0
- package/dist/tools/consult-security-requirements.js.map +1 -1
- package/dist/tools/generate-sbd-toe-skill.js +7 -2
- package/dist/tools/generate-sbd-toe-skill.js.map +1 -1
- package/dist/tools/get-threat-landscape.js +36 -13
- package/dist/tools/get-threat-landscape.js.map +1 -1
- package/dist/tools/map-review-scope.d.ts +6 -0
- package/dist/tools/map-review-scope.js +19 -0
- package/dist/tools/map-review-scope.js.map +1 -1
- package/dist/tools/ontology-loader.d.ts +8 -0
- package/dist/tools/ontology-loader.js +9 -0
- package/dist/tools/ontology-loader.js.map +1 -1
- package/dist/tools/prepare-codegen-context.d.ts +55 -1
- package/dist/tools/prepare-codegen-context.js +151 -28
- package/dist/tools/prepare-codegen-context.js.map +1 -1
- package/dist/tools/select-requirements.d.ts +53 -0
- package/dist/tools/select-requirements.js +99 -0
- package/dist/tools/select-requirements.js.map +1 -0
- package/package.json +3 -2
|
@@ -32,6 +32,7 @@ import { getRegulatoryOverlay, resolveRegulatoryFramework } from "./regulatory-o
|
|
|
32
32
|
import { expandQueryWithAliases } from "../backend/semantic-index-gateway.js";
|
|
33
33
|
import { requirementCategoryOf } from "../serving/requirement-id.js";
|
|
34
34
|
import { prepareCodegenAffordances } from "../serving/affordances.js";
|
|
35
|
+
import { runSelectionWithActivation } from "../serving/selection.js";
|
|
35
36
|
const EVIDENCE_PATTERN_CAP = 25;
|
|
36
37
|
/**
|
|
37
38
|
* v2 token diet, s3 — evidence-pattern cap applied at `detail: "standard" |
|
|
@@ -177,7 +178,7 @@ const TASK_TERM_TO_CONCERNS = [
|
|
|
177
178
|
["auth", ["auth"]],
|
|
178
179
|
["authentication", ["auth"]],
|
|
179
180
|
["authorization", ["auth"]],
|
|
180
|
-
["login", ["auth"]],
|
|
181
|
+
["login", ["auth", "encryption"]],
|
|
181
182
|
["session", ["auth"]],
|
|
182
183
|
["jwt", ["auth"]],
|
|
183
184
|
["oauth", ["auth"]],
|
|
@@ -213,8 +214,8 @@ const TASK_TERM_TO_CONCERNS = [
|
|
|
213
214
|
["release", ["release"]],
|
|
214
215
|
["deploy", ["deployment", "release"]],
|
|
215
216
|
["rollback", ["release"]],
|
|
216
|
-
["terraform", ["iac"
|
|
217
|
-
["ansible", ["iac"
|
|
217
|
+
["terraform", ["iac"]],
|
|
218
|
+
["ansible", ["iac"]],
|
|
218
219
|
["kubernetes", ["deployment", "config"]],
|
|
219
220
|
["docker", ["deployment", "config"]],
|
|
220
221
|
["container", ["deployment"]],
|
|
@@ -242,6 +243,15 @@ const TASK_TERM_TO_CONCERNS = [
|
|
|
242
243
|
["rpc", ["integration", "api"]],
|
|
243
244
|
["webhook", ["integration", "api"]],
|
|
244
245
|
["queue", ["integration"]],
|
|
246
|
+
// pós-P2 2026-08-31: integração por mensageria exige registo de eventos críticos → logging.
|
|
247
|
+
["message queue", ["integration", "logging"]],
|
|
248
|
+
// pós-P2 2026-08-31: mTLS = gestão de material criptográfico → secrets (CFG/ENC).
|
|
249
|
+
["mtls", ["encryption", "integration", "secrets"]],
|
|
250
|
+
["signature", ["integrity", "encryption"]],
|
|
251
|
+
["signing", ["integrity"]],
|
|
252
|
+
["image", ["deployment", "distribution"]],
|
|
253
|
+
["spa", ["validation", "api"]],
|
|
254
|
+
["frontend", ["validation"]],
|
|
245
255
|
["pubsub", ["integration"]],
|
|
246
256
|
["monitoring", ["monitoring", "logging"]],
|
|
247
257
|
["metric", ["monitoring"]],
|
|
@@ -275,7 +285,9 @@ const CONCERN_TO_V0_CATEGORIES_SUPPLEMENT = {
|
|
|
275
285
|
threat_modeling: ["THR"],
|
|
276
286
|
monitoring: ["LOG", "OPS"],
|
|
277
287
|
release: ["DPL", "OPS"],
|
|
278
|
-
|
|
288
|
+
// pós-P2 2026-08-31: deploy activa também a categoria base DST (cap. 02 — "Deploy
|
|
289
|
+
// apenas via pipeline validado" e afins); supplement do serving, loader inalterado.
|
|
290
|
+
deployment: ["DPL", "IAC", "CNT", "DST"],
|
|
279
291
|
integration: ["API", "INT"],
|
|
280
292
|
// `agents` → AGN comes from ontology.concernsMap (loader); nothing to supplement.
|
|
281
293
|
agents: []
|
|
@@ -328,7 +340,17 @@ const CODEGEN_PT_ALIASES = [
|
|
|
328
340
|
["integração", ["integration"]],
|
|
329
341
|
["fronteira", ["boundary"]],
|
|
330
342
|
["arquitetura", ["architecture"]],
|
|
343
|
+
// R3 do ciclo MP1 (2026-08-31, crescimento por semântica do Manual — cap. 02
|
|
344
|
+
// categoria SES = sessões; nunca por caso do oráculo): sessão/sessões → session.
|
|
345
|
+
["sessão", ["session"]],
|
|
346
|
+
["sessões", ["session"]],
|
|
331
347
|
["chave de api", ["api key"]],
|
|
348
|
+
["chave de cliente", ["api key"]],
|
|
349
|
+
["chaves de cliente", ["api key"]],
|
|
350
|
+
["mensageria", ["message queue"]],
|
|
351
|
+
["fila de mensagens", ["message queue"]],
|
|
352
|
+
["assinatura", ["signature"]],
|
|
353
|
+
["imagem", ["image"]],
|
|
332
354
|
["variável de ambiente", ["environment variable"]]
|
|
333
355
|
];
|
|
334
356
|
/**
|
|
@@ -350,6 +372,8 @@ const COMPOUND_TERM_TO_CONCERNS = [
|
|
|
350
372
|
["ci pipeline", ["build", "supply_chain"]],
|
|
351
373
|
["trust boundary", ["architecture"]],
|
|
352
374
|
["fronteira de confiança", ["architecture"]],
|
|
375
|
+
["formulário de registo", ["auth", "validation"]],
|
|
376
|
+
["registration form", ["auth", "validation"]],
|
|
353
377
|
["service to service", ["integration", "architecture"]],
|
|
354
378
|
["serviço a serviço", ["integration", "architecture"]],
|
|
355
379
|
["secret rotation", ["secrets"]],
|
|
@@ -390,7 +414,7 @@ function taskMatchesKeyword(taskLower, keyword) {
|
|
|
390
414
|
}
|
|
391
415
|
const VAGUE_PATTERNS = [
|
|
392
416
|
{
|
|
393
|
-
pattern: /\b(torna|make).{0,40}\b(
|
|
417
|
+
pattern: /\b(torna|make).{0,40}\b(segur[ao]|secure)\b/i,
|
|
394
418
|
reason: "Pedido excessivamente abrangente ('make secure' / 'tornar seguro')"
|
|
395
419
|
},
|
|
396
420
|
{
|
|
@@ -424,7 +448,7 @@ const VAGUE_PATTERNS = [
|
|
|
424
448
|
* these, so the request is unsupported rather than decomposable.
|
|
425
449
|
*/
|
|
426
450
|
const UNSUPPORTED_TECH_PATTERN = /\b(homomorphic|quantum[- ]?(resistant|safe)?|post[- ]?quantum|blockchain|smart contract|zero[- ]?knowledge|zk[- ]?(snark|stark|proof)s?|secure multiparty|federated learning)\b/i;
|
|
427
|
-
function normalizeInput(raw) {
|
|
451
|
+
export function normalizeInput(raw) {
|
|
428
452
|
const data = (typeof raw === "object" && raw !== null ? raw : {});
|
|
429
453
|
const task = typeof data.task === "string" ? data.task : "";
|
|
430
454
|
const taskTrimmed = task.trim();
|
|
@@ -536,7 +560,7 @@ function recordActivation(trace, concerns, scores, rejected, entry, targetConcer
|
|
|
536
560
|
trace.push(entry);
|
|
537
561
|
}
|
|
538
562
|
}
|
|
539
|
-
function activate(input) {
|
|
563
|
+
export function activate(input) {
|
|
540
564
|
const trace = [];
|
|
541
565
|
const rejected = [];
|
|
542
566
|
const notes = [];
|
|
@@ -670,6 +694,45 @@ function activate(input) {
|
|
|
670
694
|
reason: `Concern '${concern}' maps to AppSec Core slice family '${family}'.`
|
|
671
695
|
});
|
|
672
696
|
}
|
|
697
|
+
// 4b) Declared context activators (G-mp1a / D3, 2026-08-31): exposure and
|
|
698
|
+
// data_sensitivity stop being decorative — they activate concerns by DECLARED
|
|
699
|
+
// rule (each with its own trace source), because the reference selection
|
|
700
|
+
// semantics says an authenticated/public surface must be auditable and a
|
|
701
|
+
// personal/regulated data context must carry crypto+masking+validation.
|
|
702
|
+
const EXPOSURE_CONCERNS = {
|
|
703
|
+
internal: ["auth", "logging"],
|
|
704
|
+
authenticated: ["auth", "logging"],
|
|
705
|
+
public: ["auth", "logging", "api", "validation", "architecture"]
|
|
706
|
+
};
|
|
707
|
+
if (input.exposure && EXPOSURE_CONCERNS[input.exposure]) {
|
|
708
|
+
for (const concern of EXPOSURE_CONCERNS[input.exposure] ?? []) {
|
|
709
|
+
recordActivation(trace, concerns, concernScores, rejected, {
|
|
710
|
+
source: "exposure",
|
|
711
|
+
produced: concern,
|
|
712
|
+
trigger: input.exposure,
|
|
713
|
+
score: 0.9,
|
|
714
|
+
confidence: "deterministic",
|
|
715
|
+
reason: `exposure='${input.exposure}' activates ${concern} by declared rule (auditable exposed surface).`
|
|
716
|
+
}, concern, { capDuplicates: true });
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
const SENSITIVITY_CONCERNS = {
|
|
720
|
+
personal: ["encryption", "validation", "logging"],
|
|
721
|
+
regulated: ["encryption", "validation", "logging"],
|
|
722
|
+
secrets: ["secrets"]
|
|
723
|
+
};
|
|
724
|
+
if (input.data_sensitivity && SENSITIVITY_CONCERNS[input.data_sensitivity]) {
|
|
725
|
+
for (const concern of SENSITIVITY_CONCERNS[input.data_sensitivity] ?? []) {
|
|
726
|
+
recordActivation(trace, concerns, concernScores, rejected, {
|
|
727
|
+
source: "data_sensitivity",
|
|
728
|
+
produced: concern,
|
|
729
|
+
trigger: input.data_sensitivity,
|
|
730
|
+
score: 0.9,
|
|
731
|
+
confidence: "deterministic",
|
|
732
|
+
reason: `data_sensitivity='${input.data_sensitivity}' activates ${concern} by declared rule (ENC/masking/validation for personal or regulated data).`
|
|
733
|
+
}, concern, { capDuplicates: true });
|
|
734
|
+
}
|
|
735
|
+
}
|
|
673
736
|
// 5) Risk level (informational trace entry, no concern activation).
|
|
674
737
|
if (input.risk_level) {
|
|
675
738
|
trace.push({
|
|
@@ -681,8 +744,39 @@ function activate(input) {
|
|
|
681
744
|
reason: `Risk level ${input.risk_level} filters runtime v0 requirements.`
|
|
682
745
|
});
|
|
683
746
|
}
|
|
747
|
+
// P3 (2026-08-31): primary-concern families for the decomposition gate.
|
|
748
|
+
const primaryOfSignal = new Map();
|
|
749
|
+
for (const [term, mapped] of TASK_TERM_TO_CONCERNS) {
|
|
750
|
+
if (mapped.length > 0)
|
|
751
|
+
primaryOfSignal.set(term, mapped[0]);
|
|
752
|
+
}
|
|
753
|
+
for (const [phrase, mapped] of COMPOUND_TERM_TO_CONCERNS) {
|
|
754
|
+
if (mapped.length > 0)
|
|
755
|
+
primaryOfSignal.set(phrase, mapped[0]);
|
|
756
|
+
}
|
|
757
|
+
const primaryConcerns = new Set();
|
|
758
|
+
for (const entry of trace) {
|
|
759
|
+
if (entry.source === "risk_level" ||
|
|
760
|
+
entry.source === "exposure" ||
|
|
761
|
+
entry.source === "data_sensitivity" ||
|
|
762
|
+
entry.source === "scope_gate") {
|
|
763
|
+
continue; // contexto/informativos — não são superfícies
|
|
764
|
+
}
|
|
765
|
+
if (!concerns.has(entry.produced))
|
|
766
|
+
continue;
|
|
767
|
+
const rowPrimary = primaryOfSignal.get(entry.trigger);
|
|
768
|
+
if (rowPrimary === undefined || rowPrimary === entry.produced) {
|
|
769
|
+
primaryConcerns.add(entry.produced);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
const decompositionFamilies = [
|
|
773
|
+
...new Set([...primaryConcerns]
|
|
774
|
+
.map((concern) => CONCERN_TO_SLICE_FAMILY[concern])
|
|
775
|
+
.filter((family) => typeof family === "string"))
|
|
776
|
+
].sort();
|
|
684
777
|
return {
|
|
685
778
|
concerns: [...concerns],
|
|
779
|
+
decompositionFamilies,
|
|
686
780
|
sliceFamilies: [...sliceFamilyScores.keys()].sort((a, b) => (sliceFamilyScores.get(b) ?? 0) - (sliceFamilyScores.get(a) ?? 0) ||
|
|
687
781
|
a.localeCompare(b)),
|
|
688
782
|
trace,
|
|
@@ -736,21 +830,24 @@ function gateAfterActivation(args) {
|
|
|
736
830
|
const { input, activation, estimatedRequirements } = args;
|
|
737
831
|
const reasons = [];
|
|
738
832
|
const suggestions = [];
|
|
739
|
-
|
|
740
|
-
|
|
833
|
+
// P3 do ciclo MP1 (2026-08-31): o gate conta SUPERFÍCIES (famílias dos concerns
|
|
834
|
+
// primários de cada sinal), não o total de famílias activadas — concerns de
|
|
835
|
+
// suporte de um mesmo sinal (mtls→secrets, mensageria→logging) não pedem
|
|
836
|
+
// decomposição. GC-10 é o caso de referência: 1 integração legítima.
|
|
837
|
+
if (activation.decompositionFamilies.length > 3) {
|
|
838
|
+
reasons.push(`Pedido activa ${activation.decompositionFamilies.length} superfícies (famílias primárias: ${activation.decompositionFamilies.join(", ")}) — máximo recomendado: 3. Concerns de suporte do mesmo sinal não contam.`);
|
|
741
839
|
suggestions.push("Reparte por slice family. Cada PR/PR-step deve ficar em 1–3 slices.");
|
|
742
840
|
}
|
|
743
|
-
//
|
|
744
|
-
//
|
|
745
|
-
//
|
|
746
|
-
//
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
activation.trace.length === 0) {
|
|
841
|
+
// G-mp1a decision 2 (2026-08-31, D1): the former hard cap "max 50 activated
|
|
842
|
+
// requirements" is GONE — a legitimate L2 task activates >50 by design (the
|
|
843
|
+
// cap 02 baseline is a real catalogue). The gate guards TASK scope (vague /
|
|
844
|
+
// multi-family asks above) and PAYLOAD (the detail diet + budgets), never a
|
|
845
|
+
// requirement count. estimatedRequirements stays as a debug figure only.
|
|
846
|
+
void estimatedRequirements;
|
|
847
|
+
// D1 (G-mp1a): with the requirement-count cap gone, the no-signal guard is the
|
|
848
|
+
// vagueness catch-all. The informational risk_level trace entry must not defeat
|
|
849
|
+
// it — only real signals (concerns) count.
|
|
850
|
+
if (activation.concerns.length === 0 && input.tokenCount >= 4) {
|
|
754
851
|
return {
|
|
755
852
|
status: "needs_clarification",
|
|
756
853
|
reasons: [
|
|
@@ -804,7 +901,7 @@ function projectRelation(relation) {
|
|
|
804
901
|
source: "runtime_v1"
|
|
805
902
|
};
|
|
806
903
|
}
|
|
807
|
-
function categoriesForConcerns(concerns) {
|
|
904
|
+
export function categoriesForConcerns(concerns) {
|
|
808
905
|
const ontology = getOntologyData();
|
|
809
906
|
const categories = new Set();
|
|
810
907
|
for (const concern of concerns) {
|
|
@@ -820,12 +917,20 @@ function categoriesForConcerns(concerns) {
|
|
|
820
917
|
function resolveRuntimeV0(args) {
|
|
821
918
|
const ontology = getOntologyData();
|
|
822
919
|
const concernCategories = categoriesForConcerns(args.concerns);
|
|
823
|
-
let filteredRequirements
|
|
824
|
-
if (args.
|
|
825
|
-
|
|
920
|
+
let filteredRequirements;
|
|
921
|
+
if (args.selectedRequirements) {
|
|
922
|
+
// MP1 engine (G-mp1a O2): the selection operation already produced the set
|
|
923
|
+
// (baseline ∪ context ⊕ narrowing, all declared) — use it verbatim.
|
|
924
|
+
filteredRequirements = args.selectedRequirements;
|
|
826
925
|
}
|
|
827
|
-
|
|
828
|
-
filteredRequirements =
|
|
926
|
+
else {
|
|
927
|
+
filteredRequirements = ontology.requirements;
|
|
928
|
+
if (args.riskLevel) {
|
|
929
|
+
filteredRequirements = filteredRequirements.filter((requirement) => requirement.applicable_levels?.[args.riskLevel] === true);
|
|
930
|
+
}
|
|
931
|
+
if (concernCategories.size > 0) {
|
|
932
|
+
filteredRequirements = filteredRequirements.filter((requirement) => concernCategories.has(requirement.category));
|
|
933
|
+
}
|
|
829
934
|
}
|
|
830
935
|
const links = ontology.requirementControlLinks ?? [];
|
|
831
936
|
const requirementIds = new Set(filteredRequirements.map((r) => r.requirement_id));
|
|
@@ -2009,7 +2114,11 @@ function prepareCodegenContextCore(raw) {
|
|
|
2009
2114
|
: deterministicConcerns.length > 0
|
|
2010
2115
|
? deterministicConcerns
|
|
2011
2116
|
: activation.concerns;
|
|
2012
|
-
|
|
2117
|
+
void focusConcerns; // kept for the debug notes below; the gate no longer counts requirements
|
|
2118
|
+
// MP1 selection (G-mp1a O2): the engine composes baseline ∪ context and narrows
|
|
2119
|
+
// by the task's declared signals — this is the requirement set served.
|
|
2120
|
+
const selection = runSelectionWithActivation(input, activation);
|
|
2121
|
+
const estimatedRequirements = selection.selected.length;
|
|
2013
2122
|
const postGate = gateAfterActivation({
|
|
2014
2123
|
input,
|
|
2015
2124
|
activation,
|
|
@@ -2041,9 +2150,12 @@ function prepareCodegenContextCore(raw) {
|
|
|
2041
2150
|
], activation.trace, { rejected: activation.rejected, notes: activation.notes });
|
|
2042
2151
|
}
|
|
2043
2152
|
// ----- Resolve activated scope ----------------------------------------
|
|
2153
|
+
const ontologyForSelection = getOntologyData();
|
|
2154
|
+
const selectedIds = new Set(selection.selected.map((r) => r.requirement_id));
|
|
2044
2155
|
const v0 = resolveRuntimeV0({
|
|
2045
2156
|
riskLevel: input.risk_level,
|
|
2046
|
-
concerns: activation.concerns
|
|
2157
|
+
concerns: activation.concerns,
|
|
2158
|
+
selectedRequirements: ontologyForSelection.requirements.filter((r) => selectedIds.has(r.requirement_id))
|
|
2047
2159
|
});
|
|
2048
2160
|
const activatedSlices = resolveActivatedSlices(g2Data, activation.sliceFamilies);
|
|
2049
2161
|
const activatedSliceIds = new Set(activatedSlices.map((slice) => slice.slice_id));
|
|
@@ -2252,6 +2364,17 @@ function prepareCodegenContextCore(raw) {
|
|
|
2252
2364
|
returned_artifacts: activatedArtifacts.length,
|
|
2253
2365
|
named_v1_entities: namedV1,
|
|
2254
2366
|
unnamed_v1_entities: totalV1 - namedV1,
|
|
2367
|
+
selection: {
|
|
2368
|
+
eligible: selection.eligible_count,
|
|
2369
|
+
selected: selection.selected.length,
|
|
2370
|
+
narrowed_out_categories: selection.narrowed_out.length,
|
|
2371
|
+
narrowed_out_requirements: selection.narrowed_out.reduce((n, g) => n + g.count, 0),
|
|
2372
|
+
narrowed_out_ref: {
|
|
2373
|
+
tool: "select_sbd_toe_requirements",
|
|
2374
|
+
note: "Categorias elegíveis sem sinal na tarefa foram excluídas pelo narrowing MP1 — " +
|
|
2375
|
+
"a lista completa (por categoria, com razão) vem de select_sbd_toe_requirements com o mesmo contexto."
|
|
2376
|
+
}
|
|
2377
|
+
},
|
|
2255
2378
|
v1_consistency_mismatches: g2Data.consistency.mismatches,
|
|
2256
2379
|
v1_manifest_warnings: g2Data.consistency.warnings,
|
|
2257
2380
|
evidence_patterns_total: scoredEvidencePatterns.length,
|