@kya-os/mcp-i-core 1.2.2-canary.33 → 1.2.2-canary.35
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test$colon$coverage.log +2255 -1863
- package/coverage/coverage-final.json +2 -2
- package/dist/services/access-control.service.d.ts.map +1 -1
- package/dist/services/access-control.service.js +92 -16
- package/dist/services/access-control.service.js.map +1 -1
- package/package.json +2 -2
- package/src/services/__tests__/access-control.proof-response-validation.test.ts +95 -84
- package/src/services/access-control.service.ts +135 -32
|
@@ -376,7 +376,7 @@ export class AccessControlApiService {
|
|
|
376
376
|
});
|
|
377
377
|
|
|
378
378
|
const responseText = await httpResponse.text();
|
|
379
|
-
|
|
379
|
+
|
|
380
380
|
// CRITICAL: Log raw response IMMEDIATELY before parsing/validation
|
|
381
381
|
// This will help us debug validation failures
|
|
382
382
|
console.error(`[AccessControl] 🔍 RAW API RESPONSE (before parsing):`, {
|
|
@@ -388,15 +388,17 @@ export class AccessControlApiService {
|
|
|
388
388
|
responseTextPreview: responseText.substring(0, 500),
|
|
389
389
|
fullResponseText: responseText, // Full response for debugging
|
|
390
390
|
});
|
|
391
|
-
|
|
391
|
+
|
|
392
392
|
const responseData = this.parseResponseJSON(httpResponse, responseText);
|
|
393
|
-
|
|
393
|
+
|
|
394
394
|
// Log parsed response immediately after parsing
|
|
395
395
|
console.error(`[AccessControl] 🔍 PARSED RESPONSE DATA:`, {
|
|
396
396
|
correlationId,
|
|
397
397
|
status: httpResponse.status,
|
|
398
398
|
responseDataType: typeof responseData,
|
|
399
|
-
responseDataKeys: Object.keys(
|
|
399
|
+
responseDataKeys: Object.keys(
|
|
400
|
+
(responseData as Record<string, unknown>) || {}
|
|
401
|
+
),
|
|
400
402
|
responseData: JSON.stringify(responseData, null, 2),
|
|
401
403
|
});
|
|
402
404
|
|
|
@@ -509,31 +511,131 @@ export class AccessControlApiService {
|
|
|
509
511
|
if (wrappedResponse.success !== undefined && wrappedResponse.data) {
|
|
510
512
|
// Response is wrapped in { success, data }
|
|
511
513
|
// Extract data and add success field if missing (for schema validation)
|
|
512
|
-
|
|
513
|
-
|
|
514
|
+
// CRITICAL: Ensure data is actually an object, not a string or other type
|
|
515
|
+
let dataToValidate: Record<string, unknown>;
|
|
516
|
+
if (typeof wrappedResponse.data === "string") {
|
|
517
|
+
// If data is a string, parse it (shouldn't happen but defensive)
|
|
518
|
+
try {
|
|
519
|
+
dataToValidate = JSON.parse(wrappedResponse.data) as Record<
|
|
520
|
+
string,
|
|
521
|
+
unknown
|
|
522
|
+
>;
|
|
523
|
+
} catch (parseError) {
|
|
524
|
+
throw new AgentShieldAPIError(
|
|
525
|
+
"invalid_response",
|
|
526
|
+
"Failed to parse response data",
|
|
527
|
+
{
|
|
528
|
+
parseError:
|
|
529
|
+
parseError instanceof Error
|
|
530
|
+
? parseError.message
|
|
531
|
+
: String(parseError),
|
|
532
|
+
}
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
} else if (
|
|
536
|
+
typeof wrappedResponse.data === "object" &&
|
|
537
|
+
wrappedResponse.data !== null
|
|
538
|
+
) {
|
|
539
|
+
dataToValidate = wrappedResponse.data as Record<string, unknown>;
|
|
540
|
+
} else {
|
|
541
|
+
throw new AgentShieldAPIError(
|
|
542
|
+
"invalid_response",
|
|
543
|
+
"Response data is not an object",
|
|
544
|
+
{
|
|
545
|
+
dataType: typeof wrappedResponse.data,
|
|
546
|
+
data: wrappedResponse.data,
|
|
547
|
+
}
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
|
|
514
551
|
// CRITICAL: Log the actual data structure for debugging
|
|
515
552
|
console.error(`[AccessControl] 🔍 DATA OBJECT STRUCTURE:`, {
|
|
516
553
|
correlationId,
|
|
517
554
|
dataKeys: Object.keys(dataToValidate),
|
|
518
|
-
hasAccepted:
|
|
519
|
-
hasRejected:
|
|
520
|
-
hasOutcomes:
|
|
521
|
-
hasErrors:
|
|
555
|
+
hasAccepted: "accepted" in dataToValidate,
|
|
556
|
+
hasRejected: "rejected" in dataToValidate,
|
|
557
|
+
hasOutcomes: "outcomes" in dataToValidate,
|
|
558
|
+
hasErrors: "errors" in dataToValidate,
|
|
522
559
|
acceptedType: typeof dataToValidate.accepted,
|
|
560
|
+
acceptedValue: dataToValidate.accepted,
|
|
523
561
|
rejectedType: typeof dataToValidate.rejected,
|
|
562
|
+
rejectedValue: dataToValidate.rejected,
|
|
524
563
|
outcomesType: typeof dataToValidate.outcomes,
|
|
564
|
+
outcomesValue: dataToValidate.outcomes,
|
|
525
565
|
errorsType: typeof dataToValidate.errors,
|
|
526
566
|
errorsIsArray: Array.isArray(dataToValidate.errors),
|
|
527
567
|
fullData: JSON.stringify(dataToValidate, null, 2),
|
|
528
568
|
});
|
|
529
|
-
|
|
569
|
+
|
|
530
570
|
// Ensure success field is present (required by schema)
|
|
531
571
|
// wrappedResponse.success should be true since we checked it exists
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
572
|
+
// CRITICAL: Explicitly construct the object to ensure all fields are present
|
|
573
|
+
// Don't rely on spread operator which might fail with Proxies or non-enumerable properties
|
|
574
|
+
const dataWithSuccess: Record<string, unknown> = {
|
|
575
|
+
success: wrappedResponse.success === true,
|
|
576
|
+
accepted: dataToValidate.accepted,
|
|
577
|
+
rejected: dataToValidate.rejected,
|
|
535
578
|
};
|
|
536
579
|
|
|
580
|
+
// Add optional fields if present
|
|
581
|
+
if (
|
|
582
|
+
"outcomes" in dataToValidate &&
|
|
583
|
+
dataToValidate.outcomes !== undefined
|
|
584
|
+
) {
|
|
585
|
+
dataWithSuccess.outcomes = dataToValidate.outcomes;
|
|
586
|
+
}
|
|
587
|
+
if ("errors" in dataToValidate && dataToValidate.errors !== undefined) {
|
|
588
|
+
dataWithSuccess.errors = dataToValidate.errors;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// CRITICAL: Log what we're validating
|
|
592
|
+
console.error(`[AccessControl] 🔍 VALIDATING DATA WITH SUCCESS:`, {
|
|
593
|
+
correlationId,
|
|
594
|
+
dataWithSuccessKeys: Object.keys(dataWithSuccess),
|
|
595
|
+
hasSuccess: "success" in dataWithSuccess,
|
|
596
|
+
successValue: dataWithSuccess.success,
|
|
597
|
+
hasAccepted: "accepted" in dataWithSuccess,
|
|
598
|
+
acceptedValue: dataWithSuccess.accepted,
|
|
599
|
+
hasRejected: "rejected" in dataWithSuccess,
|
|
600
|
+
rejectedValue: dataWithSuccess.rejected,
|
|
601
|
+
hasOutcomes: "outcomes" in dataWithSuccess,
|
|
602
|
+
outcomesValue: dataWithSuccess.outcomes,
|
|
603
|
+
fullDataWithSuccess: JSON.stringify(dataWithSuccess, null, 2),
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
// CRITICAL: Ensure all required fields are present before validation
|
|
607
|
+
if (
|
|
608
|
+
typeof dataWithSuccess.accepted !== "number" ||
|
|
609
|
+
typeof dataWithSuccess.rejected !== "number"
|
|
610
|
+
) {
|
|
611
|
+
console.error(
|
|
612
|
+
`[AccessControl] ❌ MISSING REQUIRED FIELDS AFTER CONSTRUCTION:`,
|
|
613
|
+
{
|
|
614
|
+
correlationId,
|
|
615
|
+
hasAccepted: "accepted" in dataWithSuccess,
|
|
616
|
+
acceptedType: typeof dataWithSuccess.accepted,
|
|
617
|
+
acceptedValue: dataWithSuccess.accepted,
|
|
618
|
+
hasRejected: "rejected" in dataWithSuccess,
|
|
619
|
+
rejectedType: typeof dataWithSuccess.rejected,
|
|
620
|
+
rejectedValue: dataWithSuccess.rejected,
|
|
621
|
+
dataWithSuccessKeys: Object.keys(dataWithSuccess),
|
|
622
|
+
fullDataWithSuccess: JSON.stringify(dataWithSuccess, null, 2),
|
|
623
|
+
dataToValidateKeys: Object.keys(dataToValidate),
|
|
624
|
+
fullDataToValidate: JSON.stringify(dataToValidate, null, 2),
|
|
625
|
+
originalResponseData: JSON.stringify(responseData, null, 2),
|
|
626
|
+
}
|
|
627
|
+
);
|
|
628
|
+
throw new AgentShieldAPIError(
|
|
629
|
+
"invalid_response",
|
|
630
|
+
"Response data missing required fields (accepted/rejected)",
|
|
631
|
+
{
|
|
632
|
+
responseData,
|
|
633
|
+
dataToValidate,
|
|
634
|
+
dataWithSuccess,
|
|
635
|
+
}
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
|
|
537
639
|
const dataParsed =
|
|
538
640
|
proofSubmissionResponseSchema.safeParse(dataWithSuccess);
|
|
539
641
|
if (dataParsed.success) {
|
|
@@ -561,6 +663,7 @@ export class AccessControlApiService {
|
|
|
561
663
|
2000
|
|
562
664
|
),
|
|
563
665
|
dataKeys: Object.keys(dataToValidate),
|
|
666
|
+
dataWithSuccessKeys: Object.keys(dataWithSuccess),
|
|
564
667
|
originalResponse: JSON.stringify(responseData, null, 2).substring(
|
|
565
668
|
0,
|
|
566
669
|
2000
|
|
@@ -579,22 +682,25 @@ export class AccessControlApiService {
|
|
|
579
682
|
`[AccessControl] Original wrapped response:`,
|
|
580
683
|
JSON.stringify(responseData, null, 2)
|
|
581
684
|
);
|
|
582
|
-
|
|
685
|
+
|
|
583
686
|
// CRITICAL: Log each zod error individually for easier debugging
|
|
584
687
|
console.error(
|
|
585
688
|
`[AccessControl] ❌ ZOD VALIDATION FAILED - ${dataParsed.error.errors.length} error(s):`
|
|
586
689
|
);
|
|
587
690
|
dataParsed.error.errors.forEach((err, idx) => {
|
|
588
691
|
const errorDetails: Record<string, unknown> = {
|
|
589
|
-
path: err.path.join(
|
|
692
|
+
path: err.path.join(".") || "(root)",
|
|
590
693
|
message: err.message,
|
|
591
694
|
code: err.code,
|
|
592
695
|
};
|
|
593
696
|
// Only include properties that exist on specific error types
|
|
594
|
-
if (
|
|
595
|
-
if (
|
|
596
|
-
if (
|
|
597
|
-
console.error(
|
|
697
|
+
if ("received" in err) errorDetails.received = err.received;
|
|
698
|
+
if ("expected" in err) errorDetails.expected = err.expected;
|
|
699
|
+
if ("input" in err) errorDetails.input = err.input;
|
|
700
|
+
console.error(
|
|
701
|
+
`[AccessControl] Error ${idx + 1}:`,
|
|
702
|
+
JSON.stringify(errorDetails, null, 2)
|
|
703
|
+
);
|
|
598
704
|
});
|
|
599
705
|
console.error(
|
|
600
706
|
`[AccessControl] ❌ Full ZOD errors JSON:`,
|
|
@@ -633,33 +739,30 @@ export class AccessControlApiService {
|
|
|
633
739
|
// CRITICAL: Log to console.error with full details for debugging
|
|
634
740
|
// This format matches test expectations: single call with message and error object
|
|
635
741
|
// This log must include 'Response validation failed' in the message for test compatibility
|
|
636
|
-
console.error(
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
responseData: responseData,
|
|
641
|
-
}
|
|
642
|
-
);
|
|
742
|
+
console.error(`[AccessControl] Response validation failed`, {
|
|
743
|
+
zodErrors: parsed.error.errors,
|
|
744
|
+
responseData: responseData,
|
|
745
|
+
});
|
|
643
746
|
// Additional detailed logging for debugging
|
|
644
747
|
console.error(
|
|
645
748
|
`[AccessControl] Response validation failed`,
|
|
646
749
|
validationErrorLog
|
|
647
750
|
);
|
|
648
|
-
|
|
751
|
+
|
|
649
752
|
// CRITICAL: Log each zod error individually for easier debugging
|
|
650
753
|
console.error(
|
|
651
754
|
`[AccessControl] ❌ ZOD VALIDATION FAILED (direct) - ${parsed.error.errors.length} error(s):`
|
|
652
755
|
);
|
|
653
756
|
parsed.error.errors.forEach((err, idx) => {
|
|
654
757
|
const errorDetails: Record<string, unknown> = {
|
|
655
|
-
path: err.path.join(
|
|
758
|
+
path: err.path.join(".") || "(root)",
|
|
656
759
|
message: err.message,
|
|
657
760
|
code: err.code,
|
|
658
761
|
};
|
|
659
762
|
// Only include properties that exist on specific error types
|
|
660
|
-
if (
|
|
661
|
-
if (
|
|
662
|
-
if (
|
|
763
|
+
if ("received" in err) errorDetails.received = err.received;
|
|
764
|
+
if ("expected" in err) errorDetails.expected = err.expected;
|
|
765
|
+
if ("input" in err) errorDetails.input = err.input;
|
|
663
766
|
console.error(`[AccessControl] Error ${idx + 1}:`, errorDetails);
|
|
664
767
|
});
|
|
665
768
|
console.error(
|