CS-424 [Improvement] Include a default justification at all times on the SoA#2921
CS-424 [Improvement] Include a default justification at all times on the SoA#2921github-actions[bot] wants to merge 10 commits into
Conversation
|
@cubic-dev-ai please review it |
@chasprowebdev I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
2 issues found across 8 files
Confidence score: 3/5
- There is a concrete runtime risk in
apps/api/src/soa/utils/soa-answer-parser.ts: callingtrimwithout verifying the justification value is a string can throw on non-string JSON and break answer parsing for affected requests. apps/api/src/soa/utils/constants.tshas a medium-impact fallback gap where many controls can still returnnull, so some YES/default flows may persist without a justification instead of a safe generic default.- Given the two medium-to-high severity issues (6–7/10) with strong confidence, this looks like some regression risk rather than a merge-blocker if those paths are uncommon.
- Pay close attention to
apps/api/src/soa/utils/soa-answer-parser.ts,apps/api/src/soa/utils/constants.ts- parser type-guarding and fallback defaults directly affect justification persistence reliability.
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
|
@cubic-dev-ai please review it |
@chasprowebdev I have started the AI code review. It will take a few minutes to complete. |
|
@cubic-dev-ai review it |
@tofikwest I have started the AI code review. It will take a few minutes to complete. |
…a-justification
|
@cubic-dev-ai Ultrareview |
@chasprowebdev Starting ultrareview - a deeper analysis than a regular review. I'll post findings when complete. |
There was a problem hiding this comment.
Ultrareview completed in 10m 26s
3 issues found across 8 files
Confidence score: 3/5
- There is a concrete user-facing data quality risk: in
apps/app/src/app/(app)/[orgId]/documents/statement-of-applicability/components/EditableSOAFields.tsx, YES answers can be persisted with blank justification, which can leave incomplete SOA records. - Rendering/default behavior is inconsistent in
apps/app/src/app/(app)/[orgId]/documents/statement-of-applicability/components/SOATableRow.tsxbecause??treats empty strings as valid, so fallback justification is skipped and users see—. - Parser logic in
apps/api/src/soa/utils/soa-answer-parser.tsaccepts insufficient placeholder-like YES justifications as valid, which can prevent intended default justification fallback across API flows. - Pay close attention to
apps/app/src/app/(app)/[orgId]/documents/statement-of-applicability/components/EditableSOAFields.tsx,apps/app/src/app/(app)/[orgId]/documents/statement-of-applicability/components/SOATableRow.tsx, andapps/api/src/soa/utils/soa-answer-parser.ts- justification validation/fallback rules are misaligned and can propagate blank or weak values.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/app/src/app/(app)/[orgId]/documents/statement-of-applicability/components/EditableSOAFields.tsx">
<violation number="1" location="apps/app/src/app/(app)/[orgId]/documents/statement-of-applicability/components/EditableSOAFields.tsx:146">
P2: YES answers can be saved without a non-empty justification, allowing blank justifications to persist.</violation>
</file>
<file name="apps/app/src/app/(app)/[orgId]/documents/statement-of-applicability/components/SOATableRow.tsx">
<violation number="1" location="apps/app/src/app/(app)/[orgId]/documents/statement-of-applicability/components/SOATableRow.tsx:80">
P2: Saved-answer justification uses `??`, so empty-string saves bypass fallback and render as `—` instead of a default justification.</violation>
</file>
<file name="apps/api/src/soa/utils/soa-answer-parser.ts">
<violation number="1" location="apps/api/src/soa/utils/soa-answer-parser.ts:188">
P2: YES justifications treat insufficient placeholder text as valid and skip fallback defaults.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| if (isApplicable === false && (!justification || justification.trim().length === 0)) { | ||
| setError('Justification is required when Applicable is NO'); | ||
| justificationTextareaRef.current?.focus(); | ||
| return; | ||
| } | ||
|
|
||
| await executeSave(false, justification); | ||
| await executeSave(isApplicable, justification); |
There was a problem hiding this comment.
P2: YES answers can be saved without a non-empty justification, allowing blank justifications to persist.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/app/src/app/(app)/[orgId]/documents/statement-of-applicability/components/EditableSOAFields.tsx, line 146:
<comment>YES answers can be saved without a non-empty justification, allowing blank justifications to persist.</comment>
<file context>
@@ -152,13 +143,13 @@ export function EditableSOAFields({
const handleJustificationSave = async () => {
- if (!justification || justification.trim().length === 0) {
+ if (isApplicable === false && (!justification || justification.trim().length === 0)) {
setError('Justification is required when Applicable is NO');
justificationTextareaRef.current?.focus();
</file context>
| if (isApplicable === false && (!justification || justification.trim().length === 0)) { | |
| setError('Justification is required when Applicable is NO'); | |
| justificationTextareaRef.current?.focus(); | |
| return; | |
| } | |
| await executeSave(false, justification); | |
| await executeSave(isApplicable, justification); | |
| const normalizedJustification = justification?.trim() ?? ''; | |
| if (isApplicable !== null && normalizedJustification.length === 0) { | |
| setError('Justification is required'); | |
| justificationTextareaRef.current?.focus(); | |
| return; | |
| } | |
| await executeSave( | |
| isApplicable, | |
| normalizedJustification.length > 0 ? normalizedJustification : null, | |
| ); |
| displayIsApplicable === false | ||
| ? (answerData.answer ?? question.columnMapping.justification ?? null) | ||
| : null; | ||
| answerData.answer ?? question.columnMapping.justification ?? null; |
There was a problem hiding this comment.
P2: Saved-answer justification uses ??, so empty-string saves bypass fallback and render as — instead of a default justification.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/app/src/app/(app)/[orgId]/documents/statement-of-applicability/components/SOATableRow.tsx, line 80:
<comment>Saved-answer justification uses `??`, so empty-string saves bypass fallback and render as `—` instead of a default justification.</comment>
<file context>
@@ -77,9 +77,7 @@ export function SOATableRow({
- displayIsApplicable === false
- ? (answerData.answer ?? question.columnMapping.justification ?? null)
- : null;
+ answerData.answer ?? question.columnMapping.justification ?? null;
} else {
// Normal logic: processedResult / column mapping until user saves (then branch above)
</file context>
| answerData.answer ?? question.columnMapping.justification ?? null; | |
| answerData.answer || question.columnMapping.justification || null; |
| finalIsApplicable === false ? parsedAnswer.justification || null : null; | ||
| finalIsApplicable === false | ||
| ? llmJustification | ||
| : llmJustification || getInclusionJustification(closure); |
There was a problem hiding this comment.
P2: YES justifications treat insufficient placeholder text as valid and skip fallback defaults.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/soa/utils/soa-answer-parser.ts, line 188:
<comment>YES justifications treat insufficient placeholder text as valid and skip fallback defaults.</comment>
<file context>
@@ -163,12 +170,22 @@ export function parseAndProcessSOAAnswer(
- finalIsApplicable === false ? parsedAnswer.justification || null : null;
+ finalIsApplicable === false
+ ? llmJustification
+ : llmJustification || getInclusionJustification(closure);
send({
</file context>
| : llmJustification || getInclusionJustification(closure); | |
| : llmJustification && !isInsufficientDataAnswer(llmJustification) | |
| ? llmJustification | |
| : getInclusionJustification(closure); |
This is an automated pull request to merge chas/soa-justification into dev.
It was created by the [Auto Pull Request] action.
Summary by cubic
Ensure every SoA control always has a justification by adding ISO 27001:2022 family defaults, a generic fallback when no family matches, and showing/saving them for both Applicable and Not Applicable. Addresses CS-424.
INCLUSION_JUSTIFICATIONSandgetInclusionJustification(ISO 27001:2022 family mapping incl. all of section 7). When the answer is missing/insufficient or a YES lacks a reason, default to a family justification or a generic default. Pass control closure through parsing/defaults and persist justifications for both YES and NO.getInclusionJustificationfamily mapping.Written for commit b9fd71b. Summary will update on new commits.