@planu/cli 4.11.1 → 4.11.2
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/CHANGELOG.md +9 -0
- package/dist/engine/cascade-hooks/registry.js +0 -2
- package/dist/engine/challenge-scenarios-loader.d.ts +3 -3
- package/dist/engine/compliance-injector.d.ts +3 -3
- package/dist/engine/dashboard/kanban-logic.d.ts +1 -1
- package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts +1 -1
- package/dist/engine/doc-generator/proposal/proposal-generator.d.ts +1 -1
- package/dist/engine/elicitation/answer-extractor.d.ts +1 -4
- package/dist/engine/elicitation/answer-extractor.js +197 -68
- package/dist/engine/elicitation/decision-gap-detector.js +59 -79
- package/dist/engine/elicitation/index.d.ts +0 -1
- package/dist/engine/elicitation/index.js +0 -1
- package/dist/engine/elicitation/question-grounding-gate.js +83 -34
- package/dist/engine/evidence-gates/lifecycle-gate.js +27 -14
- package/dist/engine/evidence-index/index-builder.js +7 -2
- package/dist/engine/frontmatter-sha/frozen-fields.d.ts +1 -1
- package/dist/engine/handoff-artifacts/schemas/red-tests-handoff.d.ts +2 -2
- package/dist/engine/handoff-artifacts/schemas.d.ts +22 -22
- package/dist/engine/human-summary.js +3 -3
- package/dist/engine/implementation-contract/common.d.ts +1 -1
- package/dist/engine/keyword-matcher/stopwords.d.ts +2 -2
- package/dist/engine/provider-adapters/adapters/claude.d.ts +1 -1
- package/dist/engine/provider-adapters/adapters/gemini.d.ts +1 -1
- package/dist/engine/provider-adapters/adapters/gpt4.d.ts +1 -1
- package/dist/engine/provider-adapters/adapters/markdown.d.ts +1 -1
- package/dist/engine/safety/atomic-write-file.d.ts +1 -1
- package/dist/engine/safety/cross-process-lock.d.ts +1 -1
- package/dist/engine/safety/path-sanitizer.d.ts +1 -1
- package/dist/engine/security-analyzer/profile-detector.js +48 -16
- package/dist/engine/spec-format/copula-blacklist.d.ts +2 -2
- package/dist/engine/spec-format/metadata-value-policy.d.ts +20 -20
- package/dist/engine/text-signal-boundaries.js +45 -12
- package/dist/engine/universal-rules/rules/planu-modes.js +0 -2
- package/dist/engine/well-known/manifest-schema.d.ts +33 -33
- package/dist/engine/workers/schema.d.ts +3 -3
- package/dist/hosts/claude-code/ux/mcp-resources.d.ts +4 -4
- package/dist/hosts/codex/rest-shim.d.ts +1 -1
- package/dist/native/lightweight-command-catalog.d.ts +30 -30
- package/dist/server/openapi-spec.d.ts +132 -132
- package/dist/tools/challenge-spec/event-challenge-scenarios.js +8 -27
- package/dist/tools/challenge-spec/resilience-challenge-scenarios-a.js +16 -32
- package/dist/tools/challenge-spec/resilience-challenge-scenarios-b.js +15 -20
- package/dist/tools/challenge-spec/scenario-collector.d.ts +13 -0
- package/dist/tools/challenge-spec/scenario-collector.js +86 -0
- package/dist/tools/challenge-spec/scenarios-data.js +10 -9
- package/dist/tools/challenge-spec/scenarios-failure.js +38 -54
- package/dist/tools/challenge-spec/scenarios-scale.js +14 -15
- package/dist/tools/challenge-spec/scenarios-security.d.ts +1 -1
- package/dist/tools/challenge-spec/scenarios-security.js +18 -11
- package/dist/tools/challenge-spec/scenarios-utils.d.ts +31 -5
- package/dist/tools/challenge-spec/scenarios-utils.js +102 -6
- package/dist/tools/challenge-spec/security-challenge-scenarios.d.ts +1 -1
- package/dist/tools/challenge-spec/security-challenge-scenarios.js +20 -25
- package/dist/tools/challenge-spec.js +39 -118
- package/dist/tools/compliance-gate-handler.d.ts +1 -1
- package/dist/tools/create-spec.js +4 -24
- package/dist/tools/delete-first/suggest-deletions.d.ts +1 -1
- package/dist/tools/generate-batch-script.d.ts +2 -2
- package/dist/tools/init-project/rules-generator.js +0 -1
- package/dist/tools/oauth-handler.d.ts +1 -1
- package/dist/tools/register-sdd-tools.d.ts +1 -1
- package/dist/tools/render-spec-for-provider.d.ts +2 -2
- package/dist/tools/scan-orphan-spec-refs.d.ts +6 -6
- package/dist/tools/schemas/agents.d.ts +7 -7
- package/dist/tools/schemas/analysis.d.ts +11 -11
- package/dist/tools/schemas/delete.d.ts +5 -5
- package/dist/tools/schemas/github.d.ts +4 -4
- package/dist/tools/schemas/ide-config.d.ts +12 -12
- package/dist/tools/schemas/infra.d.ts +9 -9
- package/dist/tools/schemas/lifecycle.d.ts +50 -50
- package/dist/tools/schemas/llm-provider-schemas.d.ts +7 -7
- package/dist/tools/schemas/plugins-schemas.d.ts +12 -12
- package/dist/tools/schemas/project-graph.d.ts +6 -6
- package/dist/tools/schemas/rbac.d.ts +2 -2
- package/dist/tools/schemas/registry.d.ts +3 -3
- package/dist/tools/schemas/runtime-security.d.ts +2 -2
- package/dist/tools/schemas/session.d.ts +2 -2
- package/dist/tools/schemas/spec.d.ts +15 -15
- package/dist/tools/schemas/token-intelligence.d.ts +6 -6
- package/dist/tools/schemas/token-optimization.d.ts +2 -2
- package/dist/tools/schemas/workers-schema.d.ts +4 -4
- package/dist/tools/skill-bootstrap-handler.d.ts +7 -7
- package/dist/tools/tool-registry/deprecated-stubs.d.ts +1 -1
- package/dist/tools/triage-request.d.ts +3 -3
- package/dist/tools/update-status/file-sync.d.ts +1 -1
- package/dist/tools/update-status/file-sync.js +10 -45
- package/dist/tools/update-status/index.js +28 -27
- package/dist/tools/update-status/response-builder.d.ts +1 -1
- package/dist/tools/update-status/response-builder.js +11 -19
- package/dist/tools/update-status/transition-guard.js +1 -1
- package/dist/tools/update-status-actions.d.ts +0 -1
- package/dist/tools/update-status-actions.js +10 -26
- package/dist/tools/update-status-convention-gate.d.ts +1 -6
- package/dist/tools/update-status-convention-gate.js +9 -33
- package/dist/types/docs.d.ts +0 -4
- package/dist/types/env.d.ts +0 -4
- package/dist/types/estimation.d.ts +0 -2
- package/dist/types/multi-teammate-review.d.ts +4 -4
- package/dist/types/plugin-install.d.ts +7 -7
- package/package.json +21 -18
- package/planu-native.json +1 -1
- package/planu-plugin.json +1 -1
- package/src/i18n/messages/en.json +0 -72
- package/src/i18n/messages/es.json +0 -72
- package/src/i18n/messages/pt.json +0 -72
- package/dist/config/elicitation-dimensions.json +0 -51
- package/dist/engine/actuals-estimator.d.ts +0 -14
- package/dist/engine/actuals-estimator.js +0 -64
- package/dist/engine/cascade-hooks/hooks/living-specs.hook.d.ts +0 -3
- package/dist/engine/cascade-hooks/hooks/living-specs.hook.js +0 -19
- package/dist/engine/elicitation/option-builder.d.ts +0 -9
- package/dist/engine/elicitation/option-builder.js +0 -85
- package/dist/engine/progress-writer.d.ts +0 -10
- package/dist/engine/progress-writer.js +0 -63
- package/dist/tools/update-status/mode-hints.d.ts +0 -4
- package/dist/tools/update-status/mode-hints.js +0 -24
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// tools/challenge-spec/scenarios-security.ts — Authentication, injection, and CSRF scenarios
|
|
2
|
-
import { contentMentions, hasAnyAffirmedMatch } from './scenarios-utils.js';
|
|
2
|
+
import { contentMentions, detectChallengeCapabilities, hasAnyAffirmedMatch, } from './scenarios-utils.js';
|
|
3
3
|
const AUTH_SIGNALS = [
|
|
4
4
|
/\b(auth|authentication|login|session|token|jwt|oauth|permission|role|rbac)\b/i,
|
|
5
5
|
];
|
|
@@ -9,10 +9,11 @@ const INPUT_SIGNALS = [
|
|
|
9
9
|
const STATE_CHANGING_WEB_SIGNALS = [
|
|
10
10
|
/\b(form submit|post request|put request|delete request|mutation|state-changing|cookie auth)\b/i,
|
|
11
11
|
];
|
|
12
|
-
export function generateSecurityScenarios(
|
|
12
|
+
export function generateSecurityScenarios(spec, content, _knowledge) {
|
|
13
13
|
const scenarios = [];
|
|
14
14
|
const lower = content.toLowerCase();
|
|
15
15
|
const text = content;
|
|
16
|
+
const capabilities = detectChallengeCapabilities(spec, content);
|
|
16
17
|
// Authentication bypass
|
|
17
18
|
if (hasAnyAffirmedMatch(text, AUTH_SIGNALS)) {
|
|
18
19
|
scenarios.push({
|
|
@@ -22,23 +23,29 @@ export function generateSecurityScenarios(_spec, content, _knowledge) {
|
|
|
22
23
|
currentHandling: contentMentions(lower, ['auth', 'jwt', 'session', 'middleware'])
|
|
23
24
|
? 'Authentication mentioned'
|
|
24
25
|
: 'No explicit auth handling',
|
|
25
|
-
requiredHandling: 'Validate
|
|
26
|
-
dataConsistency: '
|
|
27
|
-
userExperience: '
|
|
26
|
+
requiredHandling: 'Validate identity at the declared trust boundary and enforce the authorization model named by the contract.',
|
|
27
|
+
dataConsistency: 'Unauthorized operations must not read or mutate protected state.',
|
|
28
|
+
userExperience: 'Use the unauthorized-access behavior defined by the client contract.',
|
|
28
29
|
});
|
|
29
30
|
}
|
|
30
31
|
// Injection attacks
|
|
31
32
|
if (hasAnyAffirmedMatch(text, INPUT_SIGNALS)) {
|
|
32
33
|
scenarios.push({
|
|
33
|
-
scenario:
|
|
34
|
+
scenario: capabilities.database
|
|
35
|
+
? 'SQL injection / XSS / command injection via user input'
|
|
36
|
+
: 'XSS / command injection via user input',
|
|
34
37
|
probability: 'high',
|
|
35
38
|
impact: 'critical',
|
|
36
39
|
currentHandling: contentMentions(lower, ['sanitiz', 'escap', 'parameterized', 'prepared'])
|
|
37
40
|
? 'Some sanitization mentioned'
|
|
38
41
|
: 'Not explicitly addressed',
|
|
39
|
-
requiredHandling:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
requiredHandling: capabilities.database
|
|
43
|
+
? 'Use parameterized queries and sanitize output at the declared input boundary.'
|
|
44
|
+
: 'Sanitize output and constrain command execution at the declared input boundary.',
|
|
45
|
+
dataConsistency: capabilities.database
|
|
46
|
+
? 'Use least-privilege database access for data reached through this input.'
|
|
47
|
+
: 'Invalid input must not reach state-changing operations.',
|
|
48
|
+
userExperience: 'Use the invalid-input response defined by the interaction contract.',
|
|
42
49
|
});
|
|
43
50
|
}
|
|
44
51
|
// CSRF / rate limiting only applies when the spec explicitly involves auth or
|
|
@@ -52,9 +59,9 @@ export function generateSecurityScenarios(_spec, content, _knowledge) {
|
|
|
52
59
|
currentHandling: contentMentions(lower, ['csrf', 'rate limit', 'throttle', 'captcha'])
|
|
53
60
|
? 'Some protection mentioned'
|
|
54
61
|
: 'Not addressed',
|
|
55
|
-
requiredHandling: '
|
|
62
|
+
requiredHandling: 'Define request-forgery and abuse controls from the authentication and mutation contracts.',
|
|
56
63
|
dataConsistency: 'Prevent mass data modifications from automated attacks.',
|
|
57
|
-
userExperience: '
|
|
64
|
+
userExperience: 'Use the throttling and verification behavior defined by the auth contract.',
|
|
58
65
|
});
|
|
59
66
|
}
|
|
60
67
|
return scenarios;
|
|
@@ -1,7 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { Spec } from '../../types/index.js';
|
|
2
|
+
export interface ChallengeCapabilities {
|
|
3
|
+
networkApi: boolean;
|
|
4
|
+
database: boolean;
|
|
5
|
+
authentication: boolean;
|
|
6
|
+
authorization: boolean;
|
|
7
|
+
userInput: boolean;
|
|
8
|
+
externalService: boolean;
|
|
9
|
+
payment: boolean;
|
|
10
|
+
fileTransfer: boolean;
|
|
11
|
+
scale: boolean;
|
|
12
|
+
dataWrite: boolean;
|
|
13
|
+
cache: boolean;
|
|
14
|
+
duplicateOperation: boolean;
|
|
15
|
+
concurrency: boolean;
|
|
16
|
+
privacy: boolean;
|
|
17
|
+
events: boolean;
|
|
18
|
+
implementationContract: boolean;
|
|
19
|
+
desktopUpdate: boolean;
|
|
20
|
+
agent: boolean;
|
|
21
|
+
ddd: boolean;
|
|
22
|
+
xp: boolean;
|
|
23
|
+
validation: boolean;
|
|
24
|
+
rateLimit: boolean;
|
|
25
|
+
saga: boolean;
|
|
26
|
+
platform: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Boundary-aware, affirmative replacement for advisory substring checks. */
|
|
29
|
+
export declare function contentMentions(content: string, keywords: string[]): boolean;
|
|
30
|
+
export declare function detectChallengeCapabilities(spec: Spec, specContent: string): ChallengeCapabilities;
|
|
31
|
+
export declare function hasAnyChallengeCapability(capabilities: ChallengeCapabilities, names: (keyof ChallengeCapabilities)[]): boolean;
|
|
6
32
|
export { hasAffirmedMatch, hasAnyAffirmedMatch, stripNonContractText, } from '../../engine/text-signal-boundaries.js';
|
|
7
33
|
//# sourceMappingURL=scenarios-utils.d.ts.map
|
|
@@ -1,10 +1,106 @@
|
|
|
1
1
|
// tools/challenge-spec/scenarios-utils.ts — Shared utilities for scenario generators
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
import { hasAnyAffirmedMatch } from '../../engine/text-signal-boundaries.js';
|
|
3
|
+
const CAPABILITY_SIGNALS = {
|
|
4
|
+
networkApi: [
|
|
5
|
+
/(?<!compiler\s)\bapi\b(?!\s+(?:compatibility|surface|package|type))/i,
|
|
6
|
+
/\b(?:public|external|remote|rest|http)\s+api\b/i,
|
|
7
|
+
/\bapi\s+(?:endpoint|route|request|response|server|client)\b/i,
|
|
8
|
+
/\b(?:http|rest)\s+(?:endpoint|route|request|response)\b/i,
|
|
9
|
+
/\bfetch(?:es|ing)?\s+(?:from\s+)?(?:an?\s+)?(?:endpoint|service|url)\b/i,
|
|
10
|
+
],
|
|
11
|
+
database: [
|
|
12
|
+
/\b(?:database|postgres(?:ql)?|mysql|sqlite|mongodb|dynamodb|supabase)\b/i,
|
|
13
|
+
/\b(?:sql|nosql)\s+(?:query|statement|table|database)\b/i,
|
|
14
|
+
/\b(?:database|persistence)\s+(?:query|schema|table|migration|transaction)\b/i,
|
|
15
|
+
/\b(?:orm|prisma|drizzle|typeorm|sequelize)\b/i,
|
|
16
|
+
],
|
|
17
|
+
authentication: [
|
|
18
|
+
/\b(?:auth(?:entication)?|login|sign[ -]?in|password|jwt|oauth2?|session\s+auth)\b/i,
|
|
19
|
+
],
|
|
20
|
+
authorization: [
|
|
21
|
+
/\b(?:authori[sz]ation|permission|rbac|role-based|access\s+control|resource\s+ownership|tenant)\b/i,
|
|
22
|
+
],
|
|
23
|
+
userInput: [
|
|
24
|
+
/\b(?:user|client)\s+input\b/i,
|
|
25
|
+
/\b(?:input\s+form|form\s+(?:input|submission)|request\s+body|query\s+parameter|uploaded\s+payload)\b/i,
|
|
26
|
+
/\binputs?\s+(?:are\s+)?validat/i,
|
|
27
|
+
],
|
|
28
|
+
externalService: [
|
|
29
|
+
/\b(?:third-party|external|remote|downstream)\s+(?:service|provider|dependency|system)\b/i,
|
|
30
|
+
/\bwebhook\s+(?:provider|delivery|endpoint|request)\b/i,
|
|
31
|
+
],
|
|
32
|
+
payment: [/\b(?:payment|billing|checkout|invoice|stripe|paypal)\b/i],
|
|
33
|
+
fileTransfer: [
|
|
34
|
+
/\b(?:(?:large|binary|uploaded)\s+)?(?:file|image|media|attachment)\s+(?:upload|download|transfer|stream)\b/i,
|
|
35
|
+
/\b(?:upload|download|stream)(?:ed|ing|s)?\s+(?:a\s+)?(?:(?:large|binary)\s+)?(?:file|image|media|attachment)\b/i,
|
|
36
|
+
/\bbatch\s+(?:import|export|processing|operation)\b/i,
|
|
37
|
+
/\bbatch\s+upload\b/i,
|
|
38
|
+
],
|
|
39
|
+
scale: [
|
|
40
|
+
/\b(?:traffic|load|throughput|requests?\s+per\s+second|concurrent\s+users?)\b/i,
|
|
41
|
+
/\b(?:auto[ -]?scal|load\s+test|capacity\s+plan)/i,
|
|
42
|
+
/\b(?:data\s+growth|growing\s+dataset|query\s+performance\s+at\s+scale)\b/i,
|
|
43
|
+
],
|
|
44
|
+
dataWrite: [
|
|
45
|
+
/\b(?:database\s+transaction|persist(?:s|ed|ing)?\s+(?:record|entity|data)|write(?:s|ing)?\s+(?:record|entity|database))\b/i,
|
|
46
|
+
/\b(?:state-changing|data\s+mutation|atomic\s+database\s+write)\b/i,
|
|
47
|
+
/\b(?:transaction|atomic\s+write|update\s+(?:record|state)|modify\s+(?:record|state))\b/i,
|
|
48
|
+
],
|
|
49
|
+
cache: [/\b(?:cache|caching|cached\s+(?:data|response)|redis)\b/i],
|
|
50
|
+
duplicateOperation: [
|
|
51
|
+
/\b(?:double[ -]?click|duplicate\s+(?:request|submission|operation|record)|idempoten)/i,
|
|
52
|
+
],
|
|
53
|
+
concurrency: [
|
|
54
|
+
/\b(?:concurren|race\s+condition|shared\s+state|parallel\s+write|atomic\s+increment|optimistic\s+lock)/i,
|
|
55
|
+
/\b(?:counter|increment|balance|stock|quantity|vote|status\s+(?:update|transition))\b/i,
|
|
56
|
+
/\b(?:file\s+upload|resource\s+manager|search\s+feed|list\s+endpoint|global\s+cache|session\s+state|cart\s+state|draft\s+state)\b/i,
|
|
57
|
+
],
|
|
58
|
+
privacy: [
|
|
59
|
+
/\b(?:pii|personal\s+data|personal\s+information|privacy|gdpr|hipaa|data\s+subject)\b/i,
|
|
60
|
+
/\b(?:email\s+address|phone\s+number|social\s+security\s+number)\b/i,
|
|
61
|
+
],
|
|
62
|
+
events: [
|
|
63
|
+
/\b(?:process|handle|publish|consume)\w*(?:\s+[a-z0-9_-]+){0,3}\s+event\b/i,
|
|
64
|
+
/\b(?:event\s+(?:schema|contract|producer|consumer|handler|stream)|message\s+(?:broker|queue|consumer|producer))\b/i,
|
|
65
|
+
/\b(?:kafka|rabbitmq|pubsub|nats|eventbridge|sqs|sns|dead[ -]?letter\s+queue|dlq)\b/i,
|
|
66
|
+
/\b(?:publish|produce|consume)(?:es|d|r|rs|ing)?\s+(?:an?\s+)?(?:event|message)\b/i,
|
|
67
|
+
],
|
|
68
|
+
implementationContract: [
|
|
69
|
+
/\bimplementation\s+contract\b/i,
|
|
70
|
+
/\b(?:file-level\s+work\s+plan|acceptance-to-verification\s+map|behavior\s+contract)\b/i,
|
|
71
|
+
],
|
|
72
|
+
desktopUpdate: [
|
|
73
|
+
/\b(?:desktop|electron|tauri)\s+(?:app\s+)?(?:auto[ -]?)?update\b/i,
|
|
74
|
+
/\b(?:auto[ -]?updater|electron-updater)\b/i,
|
|
75
|
+
],
|
|
76
|
+
agent: [/\b(?:ai\s+agent|agent\s+contract|llm\s+agent|tool\s+call|autonomous\s+agent)\b/i],
|
|
77
|
+
ddd: [/\b(?:domain-driven\s+design|ddd\s+aggregate|aggregate\s+root|bounded\s+context)\b/i],
|
|
78
|
+
xp: [/\b(?:pair\s+programming|extreme\s+programming|xp\s+pair)\b/i],
|
|
79
|
+
validation: [/\b(?:input|payload|request|schema)\s+validat/i, /\b(?:zod|joi|json\s+schema)\b/i],
|
|
80
|
+
rateLimit: [/\b(?:rate[ -]?limit|throttl|request\s+quota)\b/i],
|
|
81
|
+
saga: [/\b(?:saga|compensating\s+transaction|distributed\s+transaction)\b/i],
|
|
82
|
+
platform: [
|
|
83
|
+
/\b(?:smart\s+contract|solidity|discord\s+bot|telegram\s+bot|iot\s+device|firmware|infrastructure\s+as\s+code|terraform|machine\s+learning\s+model)\b/i,
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
function keywordPattern(keyword) {
|
|
87
|
+
const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+');
|
|
88
|
+
const suffix = /^[a-z0-9]+$/i.test(keyword) && keyword.length > 3 ? '[a-z0-9_-]*' : '';
|
|
89
|
+
return new RegExp(`\\b${escaped}${suffix}\\b`, 'i');
|
|
90
|
+
}
|
|
91
|
+
/** Boundary-aware, affirmative replacement for advisory substring checks. */
|
|
92
|
+
export function contentMentions(content, keywords) {
|
|
93
|
+
return hasAnyAffirmedMatch(content, keywords.map(keywordPattern));
|
|
94
|
+
}
|
|
95
|
+
export function detectChallengeCapabilities(spec, specContent) {
|
|
96
|
+
const text = `${spec.title}\n${spec.tags.join(' ')}\n${specContent}`;
|
|
97
|
+
return Object.fromEntries(Object.entries(CAPABILITY_SIGNALS).map(([capability, patterns]) => [
|
|
98
|
+
capability,
|
|
99
|
+
hasAnyAffirmedMatch(text, patterns),
|
|
100
|
+
]));
|
|
101
|
+
}
|
|
102
|
+
export function hasAnyChallengeCapability(capabilities, names) {
|
|
103
|
+
return names.some((name) => capabilities[name]);
|
|
8
104
|
}
|
|
9
105
|
export { hasAffirmedMatch, hasAnyAffirmedMatch, stripNonContractText, } from '../../engine/text-signal-boundaries.js';
|
|
10
106
|
//# sourceMappingURL=scenarios-utils.js.map
|
|
@@ -3,7 +3,7 @@ import type { FailureScenario, Spec, ProjectKnowledge } from '../../types/index.
|
|
|
3
3
|
* Generate security challenge scenarios specific to authorization, STRIDE threats,
|
|
4
4
|
* and resource ownership. Used by handleChallengeSpec in the 'security' focus area.
|
|
5
5
|
*/
|
|
6
|
-
export declare function generateSecurityChallengeScenarios(spec: Spec,
|
|
6
|
+
export declare function generateSecurityChallengeScenarios(spec: Spec, specContent: string, _knowledge: ProjectKnowledge): FailureScenario[];
|
|
7
7
|
/**
|
|
8
8
|
* Generate security test case scaffolds for auth/API features.
|
|
9
9
|
* SPEC-029 criterion 12: auto-generate security test scaffolds.
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// tools/challenge-spec/security-challenge-scenarios.ts — Security challenges for challenge_spec
|
|
2
2
|
// SPEC-029 criteria 11, 24: authorization challenges and STRIDE-based challenges
|
|
3
3
|
import { detectSecurityProfiles } from '../../engine/security-analyzer.js';
|
|
4
|
+
import { hasAnyAffirmedMatch } from './scenarios-utils.js';
|
|
4
5
|
/**
|
|
5
6
|
* Generate security challenge scenarios specific to authorization, STRIDE threats,
|
|
6
7
|
* and resource ownership. Used by handleChallengeSpec in the 'security' focus area.
|
|
7
8
|
*/
|
|
8
|
-
export function generateSecurityChallengeScenarios(spec,
|
|
9
|
-
const profiles = detectSecurityProfiles(spec.title,
|
|
9
|
+
export function generateSecurityChallengeScenarios(spec, specContent, _knowledge) {
|
|
10
|
+
const profiles = detectSecurityProfiles(spec.title, specContent, spec.tags);
|
|
10
11
|
const scenarios = [];
|
|
11
12
|
// Authorization challenge (criterion 11): resource ownership
|
|
12
13
|
if (profiles.includes('public-api') || profiles.includes('db-access')) {
|
|
@@ -16,15 +17,14 @@ export function generateSecurityChallengeScenarios(spec, _specContent, _knowledg
|
|
|
16
17
|
impact: 'critical',
|
|
17
18
|
currentHandling: 'Authorization check may be missing at the data access layer — only route-level guards present.',
|
|
18
19
|
requiredHandling: 'Implement object-level authorization on every data access operation. ' +
|
|
19
|
-
'Never trust client-provided resource
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
userExperience: 'Return consistent 403 Forbidden for unauthorized access — do not reveal if the resource exists.',
|
|
20
|
+
'Never trust client-provided resource identifiers without verifying ownership using the declared authorization model.',
|
|
21
|
+
dataConsistency: 'Unauthorized access must not reveal resource existence.',
|
|
22
|
+
userExperience: 'Use the authorization failure response defined by the API contract.',
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
// JWT-specific challenges
|
|
26
|
-
const
|
|
27
|
-
const hasJwt =
|
|
26
|
+
const signalText = `${spec.title}\n${spec.tags.join(' ')}\n${specContent}`;
|
|
27
|
+
const hasJwt = hasAnyAffirmedMatch(signalText, [/\b(?:jwt|json\s+web\s+token)\b/i]);
|
|
28
28
|
if (hasJwt) {
|
|
29
29
|
scenarios.push({
|
|
30
30
|
scenario: `[${spec.id}] JWT Algorithm Confusion Attack: attacker changes JWT header algorithm to "none" or switches RS256 to HS256. Server accepts forged token if it trusts the header.`,
|
|
@@ -34,17 +34,16 @@ export function generateSecurityChallengeScenarios(spec, _specContent, _knowledg
|
|
|
34
34
|
requiredHandling: 'Explicitly configure the allowed algorithm(s) in JWT verification. ' +
|
|
35
35
|
'Never use the algorithm from the token header. Reject tokens with "none" algorithm.',
|
|
36
36
|
dataConsistency: 'Invalid tokens must never reach business logic — reject at middleware boundary.',
|
|
37
|
-
userExperience: '
|
|
37
|
+
userExperience: 'Use the invalid-token response defined by the authentication contract.',
|
|
38
38
|
});
|
|
39
39
|
scenarios.push({
|
|
40
|
-
scenario: `[${spec.id}] Expired Token Access: user presents an expired JWT. Does the
|
|
40
|
+
scenario: `[${spec.id}] Expired Token Access: user presents an expired JWT. Does the declared authentication failure occur before business logic?`,
|
|
41
41
|
probability: 'medium',
|
|
42
42
|
impact: 'high',
|
|
43
43
|
currentHandling: 'Expiry check may not be the first middleware step — race conditions possible.',
|
|
44
|
-
requiredHandling: 'Verify token expiry
|
|
45
|
-
'Return 401 with error code "token_expired" so clients can trigger refresh flow.',
|
|
44
|
+
requiredHandling: 'Verify token expiry before business logic and use the expiration result defined by the authentication contract.',
|
|
46
45
|
dataConsistency: 'Expired tokens must never modify state — check before any write operations.',
|
|
47
|
-
userExperience: '
|
|
46
|
+
userExperience: 'Use the expiration and refresh behavior defined by the client contract.',
|
|
48
47
|
});
|
|
49
48
|
}
|
|
50
49
|
// SQL injection challenges for DB features
|
|
@@ -62,28 +61,24 @@ export function generateSecurityChallengeScenarios(spec, _specContent, _knowledg
|
|
|
62
61
|
});
|
|
63
62
|
}
|
|
64
63
|
// Role-based access challenges
|
|
65
|
-
const hasRoles =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
lower.includes('rbac');
|
|
64
|
+
const hasRoles = hasAnyAffirmedMatch(signalText, [
|
|
65
|
+
/\b(?:role|admin|permission|rbac|access\s+control)\b/i,
|
|
66
|
+
]);
|
|
69
67
|
if (hasRoles) {
|
|
70
68
|
scenarios.push({
|
|
71
69
|
scenario: `[${spec.id}] Privilege Escalation via Role Manipulation: user with role "viewer" calls admin endpoint, or modifies JWT payload claiming role "admin". Role enforcement missing at middleware.`,
|
|
72
70
|
probability: 'medium',
|
|
73
71
|
impact: 'critical',
|
|
74
72
|
currentHandling: 'Role enforcement may be per-handler instead of middleware — inconsistent coverage.',
|
|
75
|
-
requiredHandling: 'Enforce
|
|
76
|
-
'Never derive roles from user-supplied data; always load from a trusted store. ' +
|
|
77
|
-
'Log unauthorized access attempts for monitoring.',
|
|
73
|
+
requiredHandling: 'Enforce the declared role model consistently before protected behavior, load roles from the trusted identity boundary, and record the audit evidence required by the contract.',
|
|
78
74
|
dataConsistency: 'Admin operations that modify state must verify role before any data mutation.',
|
|
79
|
-
userExperience: '
|
|
75
|
+
userExperience: 'Use the forbidden-action response defined by the authorization contract.',
|
|
80
76
|
});
|
|
81
77
|
}
|
|
82
78
|
// STRIDE elevation of privilege challenge for admin/payment
|
|
83
|
-
const isHighRisk =
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
lower.includes('sensitive');
|
|
79
|
+
const isHighRisk = hasAnyAffirmedMatch(signalText, [
|
|
80
|
+
/\b(?:payment|billing|admin\s+operation|sensitive\s+operation)\b/i,
|
|
81
|
+
]);
|
|
87
82
|
if (isHighRisk) {
|
|
88
83
|
scenarios.push({
|
|
89
84
|
scenario: `[${spec.id}] STRIDE Elevation of Privilege: regular user performs a sequence of legitimate API calls that results in unintended elevated access (e.g., create → transfer → access with original permissions).`,
|
|
@@ -8,23 +8,16 @@ import { updateSpec } from '../storage/spec-store.js';
|
|
|
8
8
|
import { resolveProjectId, missingProjectIdError } from './resolve-project-id.js';
|
|
9
9
|
import { elicitOrFallback, buildEnumSchema } from '../engine/elicitation/elicit-helper.js';
|
|
10
10
|
import { t, ti } from '../i18n/index.js';
|
|
11
|
-
import { generateFailureScenarios, generateSecurityScenarios, generateScaleScenarios, generateDataConsistencyScenarios, generateGameChallenges, generateDesktopAutoUpdateChallenges, } from './challenge-spec-scenarios.js';
|
|
12
11
|
import { generateConcurrencyAnalysis, buildScalabilityAssessment, calculateOverallRisk, readSpecContent, } from './challenge-spec-helpers.js';
|
|
13
12
|
import { prioritizeScenarios, buildPrioritizedSummary } from '../engine/challenge-prioritizer.js';
|
|
14
13
|
import { checkContradictions as checkScopeContradictions } from '../engine/scope-boundaries/index.js';
|
|
15
14
|
import { buildChallengeSpecSummary } from '../engine/human-summary.js';
|
|
16
|
-
import { generateAgentChallengeScenarios, isAgentSpec, } from './challenge-spec/agent-challenge-scenarios.js';
|
|
17
|
-
import { generateImplementationContractChallengeScenarios } from './challenge-spec/implementation-contract-challenge-scenarios.js';
|
|
18
|
-
import { getPlatformChallenges } from './challenge-spec/platform-challenge-scenarios.js';
|
|
19
|
-
import { generateSecurityChallengeScenarios } from './challenge-spec/security-challenge-scenarios.js';
|
|
20
|
-
import { generatePrivacyChallengeScenarios } from './challenge-spec/privacy-challenge-scenarios.js';
|
|
21
|
-
import { generateEventChallengeScenarios } from './challenge-spec/event-challenge-scenarios.js';
|
|
22
|
-
import { generateDddChallenges, generateXpPairClarityChallenge, } from './challenge-spec/architecture-challenge-scenarios.js';
|
|
23
|
-
import { generateResilienceChallengeScenarios } from './challenge-spec/resilience-challenge-scenarios.js';
|
|
24
15
|
import { detectContradictions as detectDecisionContradictions, searchPriorDecisions, } from '../engine/prior-decisions/index.js';
|
|
25
16
|
import { getDecisions } from '../storage/decision-store.js';
|
|
26
17
|
import { calculateTokenBudget, injectBudgetIntoPrompt } from '../engine/token-budget/index.js';
|
|
27
18
|
import { analyzeMinimalImplementation, loadMinimalImplementationPolicy, } from '../engine/minimality/index.js';
|
|
19
|
+
import { detectChallengeCapabilities, } from './challenge-spec/scenarios-utils.js';
|
|
20
|
+
import { collectCapabilityScenarios } from './challenge-spec/scenario-collector.js';
|
|
28
21
|
const ALL_FOCUS_AREAS = [
|
|
29
22
|
'failures',
|
|
30
23
|
'concurrency',
|
|
@@ -32,7 +25,6 @@ const ALL_FOCUS_AREAS = [
|
|
|
32
25
|
'security',
|
|
33
26
|
'data-consistency',
|
|
34
27
|
];
|
|
35
|
-
const ACTIONABLE_RELEVANCE_MIN = 15;
|
|
36
28
|
/**
|
|
37
29
|
* SPEC-615 AC3: detect contradictions between spec criteria and prior decisions.
|
|
38
30
|
* Mutates failureScenarios in place. Best-effort — never throws.
|
|
@@ -125,6 +117,7 @@ export async function handleChallengeSpec(args, server) {
|
|
|
125
117
|
const constitution = await knowledgeStore.getConstitution(projectId);
|
|
126
118
|
// 3. Read spec content
|
|
127
119
|
const specContent = await readSpecContent(spec);
|
|
120
|
+
const capabilities = detectChallengeCapabilities(spec, specContent);
|
|
128
121
|
// 4. Determine focus areas
|
|
129
122
|
const focusAreas = focus && focus.length > 0 ? focus : ALL_FOCUS_AREAS;
|
|
130
123
|
// 5. Generate failure scenarios
|
|
@@ -153,58 +146,7 @@ export async function handleChallengeSpec(args, server) {
|
|
|
153
146
|
catch {
|
|
154
147
|
// Minimality challenge is best-effort and must not hide resilience findings.
|
|
155
148
|
}
|
|
156
|
-
|
|
157
|
-
failureScenarios.push(...generateFailureScenarios(spec, specContent, knowledge));
|
|
158
|
-
}
|
|
159
|
-
if (focusAreas.includes('security')) {
|
|
160
|
-
failureScenarios.push(...generateSecurityScenarios(spec, specContent, knowledge));
|
|
161
|
-
}
|
|
162
|
-
if (focusAreas.includes('scale')) {
|
|
163
|
-
failureScenarios.push(...generateScaleScenarios(spec, specContent, knowledge));
|
|
164
|
-
}
|
|
165
|
-
if (focusAreas.includes('data-consistency')) {
|
|
166
|
-
failureScenarios.push(...generateDataConsistencyScenarios(spec, specContent, knowledge));
|
|
167
|
-
}
|
|
168
|
-
// 5e. Game-specific challenges (performance, balance, cheating, physics)
|
|
169
|
-
if (knowledge.projectCategory === 'game' ||
|
|
170
|
-
(knowledge.framework !== null &&
|
|
171
|
-
['unity', 'godot', 'unreal', 'bevy', 'phaser', 'pygame'].includes(knowledge.framework))) {
|
|
172
|
-
failureScenarios.push(...generateGameChallenges(spec, specContent, knowledge));
|
|
173
|
-
}
|
|
174
|
-
// 5f. SPEC-014b: Desktop auto-update edge cases
|
|
175
|
-
failureScenarios.push(...generateDesktopAutoUpdateChallenges(spec, specContent, knowledge));
|
|
176
|
-
// 5g. SPEC-016a: AI agent-specific failure modes
|
|
177
|
-
if (isAgentSpec(spec, specContent)) {
|
|
178
|
-
failureScenarios.push(...generateAgentChallengeScenarios(spec, specContent, knowledge));
|
|
179
|
-
}
|
|
180
|
-
if (focusAreas.includes('failures')) {
|
|
181
|
-
failureScenarios.push(...generateImplementationContractChallengeScenarios(spec, specContent));
|
|
182
|
-
}
|
|
183
|
-
// 5h. SPEC-015a: Smart contract, bot, and IoT challenges
|
|
184
|
-
failureScenarios.push(...getPlatformChallenges(spec, specContent, knowledge));
|
|
185
|
-
// 5i. SPEC-029: Security authorization and STRIDE challenges
|
|
186
|
-
if (focusAreas.includes('security')) {
|
|
187
|
-
failureScenarios.push(...generateSecurityChallengeScenarios(spec, specContent, knowledge));
|
|
188
|
-
}
|
|
189
|
-
// 5j. SPEC-030: Privacy challenges (mandatory when spec has PII)
|
|
190
|
-
failureScenarios.push(...generatePrivacyChallengeScenarios(spec, specContent, knowledge));
|
|
191
|
-
// 5k. SPEC-031: Event-driven challenges (contract, idempotence, DLQ)
|
|
192
|
-
failureScenarios.push(...generateEventChallengeScenarios(spec, specContent, knowledge));
|
|
193
|
-
// 5l. SPEC-033: DDD aggregate integrity challenges
|
|
194
|
-
const hasDdd = knowledge.architecture.primary === 'ddd' || knowledge.architecture.secondary.includes('ddd');
|
|
195
|
-
if (hasDdd) {
|
|
196
|
-
failureScenarios.push(...generateDddChallenges(spec, specContent));
|
|
197
|
-
}
|
|
198
|
-
// 5n. SPEC-034: Resilience, validation, concurrency, and saga challenges
|
|
199
|
-
failureScenarios.push(...generateResilienceChallengeScenarios(spec, specContent, knowledge));
|
|
200
|
-
// 5m. SPEC-033: XP pair programming clarity challenge
|
|
201
|
-
// `agileConfig` may be present on enriched knowledge objects (AgileProcessDetection mixin)
|
|
202
|
-
// but is not declared on the base ProjectKnowledge interface — intentional duck-type access.
|
|
203
|
-
// ProjectKnowledge has no index signature, so `as unknown` intermediate is required.
|
|
204
|
-
const agileConfig = knowledge.agileConfig;
|
|
205
|
-
if (agileConfig?.process === 'xp') {
|
|
206
|
-
failureScenarios.push(...generateXpPairClarityChallenge(spec, specContent));
|
|
207
|
-
}
|
|
149
|
+
failureScenarios.push(...collectCapabilityScenarios({ spec, specContent, knowledge, focusAreas, capabilities }));
|
|
208
150
|
// SPEC-612: Check for contradictions between outOfScope and acceptance criteria
|
|
209
151
|
if (spec.outOfScope !== undefined && spec.outOfScope.length > 0) {
|
|
210
152
|
const criteriaTexts = specContent
|
|
@@ -228,31 +170,25 @@ export async function handleChallengeSpec(args, server) {
|
|
|
228
170
|
// SPEC-615 AC3: Check criteria against prior decisions for technology contradictions
|
|
229
171
|
await runPriorDecisionContradictions(projectId, spec.title, spec.tags, specContent, failureScenarios);
|
|
230
172
|
// 6. Generate concurrency analysis
|
|
231
|
-
const concurrencyAnalysis = focusAreas.includes('concurrency')
|
|
173
|
+
const concurrencyAnalysis = focusAreas.includes('concurrency') && capabilities.concurrency
|
|
232
174
|
? generateConcurrencyAnalysis(spec, specContent, knowledge)
|
|
233
175
|
: { hotPaths: [], raceConditions: [], sharedState: [], recommendations: [] };
|
|
234
|
-
//
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
176
|
+
// Unsupported domain templates are removed before prioritization and risk calculation.
|
|
177
|
+
const actionableFailureScenarios = failureScenarios.filter((scenario) => isScenarioSupportedByCapabilities(scenario, capabilities));
|
|
178
|
+
const suppressedScenarioCount = failureScenarios.length - actionableFailureScenarios.length;
|
|
179
|
+
// 7. Build scalability assessment from grounded scenarios only.
|
|
180
|
+
const scalabilityAssessment = buildScalabilityAssessment(spec, knowledge, actionableFailureScenarios);
|
|
181
|
+
// 8. Calculate overall risk from grounded scenarios only.
|
|
182
|
+
const overallRisk = calculateOverallRisk(actionableFailureScenarios, concurrencyAnalysis);
|
|
238
183
|
// 9. Compute relevance scores and select top-3 (SPEC-338)
|
|
239
|
-
const prioritized = prioritizeScenarios(
|
|
184
|
+
const prioritized = prioritizeScenarios(actionableFailureScenarios, spec);
|
|
240
185
|
const prioritizedSummary = buildPrioritizedSummary(prioritized);
|
|
241
|
-
|
|
242
|
-
const failureScenariosScored = failureScenarios.map((s) => {
|
|
186
|
+
const failureScenariosScored = actionableFailureScenarios.map((s) => {
|
|
243
187
|
const match = prioritized.find((p) => p.scenario === s.scenario);
|
|
244
188
|
return match !== undefined ? { ...s, relevanceScore: match.relevanceScore } : s;
|
|
245
189
|
});
|
|
246
|
-
const prioritizedScenarios = new Set(prioritized.map((scenario) => scenario.scenario));
|
|
247
|
-
const actionableFailureScenarios = failureScenariosScored.filter((scenario) => prioritizedScenarios.has(scenario.scenario) ||
|
|
248
|
-
scenario.impact === 'critical' ||
|
|
249
|
-
scenario.impact === 'high' ||
|
|
250
|
-
isDomainRelevantScenario(scenario, specContent, knowledge) ||
|
|
251
|
-
(scenario.relevanceScore ?? computeScenarioRelevanceFallback(scenario)) >=
|
|
252
|
-
ACTIONABLE_RELEVANCE_MIN);
|
|
253
|
-
const suppressedScenarioCount = failureScenariosScored.length - actionableFailureScenarios.length;
|
|
254
190
|
const analysis = {
|
|
255
|
-
failureScenarios:
|
|
191
|
+
failureScenarios: failureScenariosScored,
|
|
256
192
|
concurrencyAnalysis,
|
|
257
193
|
scalabilityAssessment,
|
|
258
194
|
overallRisk,
|
|
@@ -271,12 +207,12 @@ export async function handleChallengeSpec(args, server) {
|
|
|
271
207
|
},
|
|
272
208
|
summary: {
|
|
273
209
|
totalScenarios: failureScenarios.length,
|
|
274
|
-
actionableScenarios:
|
|
210
|
+
actionableScenarios: failureScenariosScored.length,
|
|
275
211
|
suppressedLowRelevanceScenarios: suppressedScenarioCount,
|
|
276
212
|
shownByDefault: 3,
|
|
277
213
|
mustAddressBeforeCoding: prioritizedSummary,
|
|
278
|
-
criticalImpact:
|
|
279
|
-
highImpact:
|
|
214
|
+
criticalImpact: actionableFailureScenarios.filter((s) => s.impact === 'critical').length,
|
|
215
|
+
highImpact: actionableFailureScenarios.filter((s) => s.impact === 'high').length,
|
|
280
216
|
raceConditions: concurrencyAnalysis.raceConditions.length,
|
|
281
217
|
overallRisk,
|
|
282
218
|
focusAreas: focusAreas,
|
|
@@ -302,8 +238,8 @@ export async function handleChallengeSpec(args, server) {
|
|
|
302
238
|
await updateSpec(projectId, specId, {
|
|
303
239
|
challengeReport: {
|
|
304
240
|
runAt: new Date().toISOString(),
|
|
305
|
-
totalScenarios:
|
|
306
|
-
addressedCount:
|
|
241
|
+
totalScenarios: actionableFailureScenarios.length,
|
|
242
|
+
addressedCount: actionableFailureScenarios.length,
|
|
307
243
|
focusAreas: focusAreas,
|
|
308
244
|
overallRisk: overallRisk,
|
|
309
245
|
passed: true,
|
|
@@ -314,7 +250,7 @@ export async function handleChallengeSpec(args, server) {
|
|
|
314
250
|
// Best-effort: don't fail the challenge if persistence fails
|
|
315
251
|
}
|
|
316
252
|
// SPEC-595: Elicit how user wants to apply suggestions when critical issues exist
|
|
317
|
-
const criticalCount =
|
|
253
|
+
const criticalCount = actionableFailureScenarios.filter((s) => s.impact === 'critical').length;
|
|
318
254
|
if (server !== undefined && criticalCount > 0) {
|
|
319
255
|
const { field, property } = buildEnumSchema('apply', ['all', 'critical', 'none'], ['Apply all suggestions', 'Apply critical only (Recommended)', 'Review manually'], 'Apply suggestions', 'critical');
|
|
320
256
|
const schema = { type: 'object', properties: { [field]: property } };
|
|
@@ -326,7 +262,7 @@ export async function handleChallengeSpec(args, server) {
|
|
|
326
262
|
{
|
|
327
263
|
type: 'text',
|
|
328
264
|
text: ti('tools.challenge_spec.success', {
|
|
329
|
-
scenarioCount: String(
|
|
265
|
+
scenarioCount: String(actionableFailureScenarios.length),
|
|
330
266
|
}),
|
|
331
267
|
},
|
|
332
268
|
{ type: 'text', text: humanSummary },
|
|
@@ -349,7 +285,7 @@ export async function handleChallengeSpec(args, server) {
|
|
|
349
285
|
{
|
|
350
286
|
type: 'text',
|
|
351
287
|
text: ti('tools.challenge_spec.success', {
|
|
352
|
-
scenarioCount: String(
|
|
288
|
+
scenarioCount: String(actionableFailureScenarios.length),
|
|
353
289
|
}),
|
|
354
290
|
},
|
|
355
291
|
{ type: 'text', text: humanSummary },
|
|
@@ -362,12 +298,12 @@ export async function handleChallengeSpec(args, server) {
|
|
|
362
298
|
{
|
|
363
299
|
type: 'text',
|
|
364
300
|
text: ti('tools.challenge_spec.success', {
|
|
365
|
-
scenarioCount: String(
|
|
301
|
+
scenarioCount: String(actionableFailureScenarios.length),
|
|
366
302
|
}),
|
|
367
303
|
},
|
|
368
304
|
{
|
|
369
305
|
type: 'text',
|
|
370
|
-
text: `Actionable scenarios: ${String(
|
|
306
|
+
text: `Actionable scenarios: ${String(failureScenariosScored.length)}` +
|
|
371
307
|
` | Suppressed low-relevance: ${String(suppressedScenarioCount)}` +
|
|
372
308
|
` | Overall risk: ${overallRisk}`,
|
|
373
309
|
},
|
|
@@ -376,37 +312,22 @@ export async function handleChallengeSpec(args, server) {
|
|
|
376
312
|
structuredContent: analysisPayload,
|
|
377
313
|
};
|
|
378
314
|
}
|
|
379
|
-
function
|
|
380
|
-
if (scenario.impact === 'critical') {
|
|
381
|
-
return 100;
|
|
382
|
-
}
|
|
383
|
-
if (scenario.impact === 'high') {
|
|
384
|
-
return 70;
|
|
385
|
-
}
|
|
386
|
-
if (scenario.probability === 'high') {
|
|
387
|
-
return 35;
|
|
388
|
-
}
|
|
389
|
-
return 0;
|
|
390
|
-
}
|
|
391
|
-
function isDomainRelevantScenario(scenario, specContent, knowledge) {
|
|
315
|
+
function isScenarioSupportedByCapabilities(scenario, capabilities) {
|
|
392
316
|
const haystack = `${scenario.scenario} ${scenario.currentHandling} ${scenario.requiredHandling}`
|
|
393
317
|
.toLowerCase()
|
|
394
318
|
.trim();
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
[
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
/\b(
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
return true;
|
|
409
|
-
}
|
|
410
|
-
return false;
|
|
319
|
+
const rules = [
|
|
320
|
+
[/\b(?:payment|billing|checkout|stripe|paypal)\b/, capabilities.payment],
|
|
321
|
+
[/\b(?:privacy|pii|gdpr|personal data)\b/, capabilities.privacy],
|
|
322
|
+
[
|
|
323
|
+
/\b(?:bola|idor|jwt|authentication|authorization|csrf|privilege)\b/,
|
|
324
|
+
capabilities.authentication || capabilities.authorization,
|
|
325
|
+
],
|
|
326
|
+
[/\b(?:sql|nosql|database|connection pool)\b/, capabilities.database],
|
|
327
|
+
[/\b(?:event contract|event schema|message broker|kafka|rabbitmq|dlq)\b/, capabilities.events],
|
|
328
|
+
[/\b(?:api endpoint|http route|http status|network timeout)\b/, capabilities.networkApi],
|
|
329
|
+
[/\b(?:traffic spike|load test|auto-scaling|throughput)\b/, capabilities.scale],
|
|
330
|
+
];
|
|
331
|
+
return rules.every(([pattern, supported]) => !pattern.test(haystack) || supported);
|
|
411
332
|
}
|
|
412
333
|
//# sourceMappingURL=challenge-spec.js.map
|
|
@@ -3,8 +3,8 @@ import type { ToolResult } from '../types/index.js';
|
|
|
3
3
|
export declare const ConfigureComplianceGateSchema: z.ZodObject<{
|
|
4
4
|
projectPath: z.ZodString;
|
|
5
5
|
mode: z.ZodEnum<{
|
|
6
|
-
soft: "soft";
|
|
7
6
|
hard: "hard";
|
|
7
|
+
soft: "soft";
|
|
8
8
|
}>;
|
|
9
9
|
threshold: z.ZodDefault<z.ZodNumber>;
|
|
10
10
|
}, z.core.$strip>;
|