@@ -1020,6 +1020,161 @@ check_create_issue_auto_injection() {
10201020}
10211021check_create_issue_auto_injection
10221022
1023+ # INT-001: JSON Schema Draft 7 Validation (Section 9.1)
1024+ echo " Running INT-001: JSON Schema Draft 7 Validation..."
1025+ check_json_schema_draft7 () {
1026+ local tools_json=" pkg/workflow/js/safe_outputs_tools.json"
1027+ local gateway_handler=" actions/setup/js/safe_outputs_handlers.cjs"
1028+ local failed=0
1029+
1030+ # Per spec Section 9.1: All tool invocations MUST validate against JSON Schema Draft 7.
1031+ # Check that tool schemas declare draft-07 or that the gateway validates using Ajv/equivalent.
1032+
1033+ if [ ! -f " $tools_json " ]; then
1034+ log_high " INT-001: Tool definitions file missing: $tools_json "
1035+ return
1036+ fi
1037+
1038+ # Per spec Section 9.1: Schema validation is provided by the MCP framework via inputSchema.
1039+ # Check that the tool definitions include inputSchema on all tools, which enables
1040+ # JSON Schema Draft 7 validation at the MCP server level.
1041+ # Note: Explicit Ajv usage is one approach; relying on MCP framework schema enforcement
1042+ # via inputSchema is the primary conformant pattern in this implementation.
1043+
1044+ # Verify inputSchema is present on all tools (required by JSON Schema Draft 7 pattern)
1045+ local tools_without_schema
1046+ tools_without_schema=$( python3 -c "
1047+ import json, sys
1048+ try:
1049+ data = json.load(open('$tools_json '))
1050+ tools = data if isinstance(data, list) else data.get('tools', [])
1051+ missing = [t.get('name','?') for t in tools if isinstance(t, dict) and 'inputSchema' not in t]
1052+ if missing: print(','.join(missing))
1053+ except Exception as e:
1054+ sys.exit(0)
1055+ " 2> /dev/null)
1056+ if [ -n " $tools_without_schema " ]; then
1057+ log_medium " INT-001: Tools missing inputSchema in $tools_json : $tools_without_schema "
1058+ failed=1
1059+ fi
1060+
1061+ if [ $failed -eq 0 ]; then
1062+ log_pass " INT-001: Tool schemas include inputSchema for JSON Schema Draft 7 validation"
1063+ fi
1064+ }
1065+ check_json_schema_draft7
1066+
1067+ # INT-002: Sanitization Pipeline Completeness (Section 9.4 S1, S4)
1068+ echo " Running INT-002: Sanitization Pipeline Completeness..."
1069+ check_sanitization_pipeline () {
1070+ local core_sanitizer=" actions/setup/js/sanitize_content_core.cjs"
1071+ local fallback_sanitizer=" actions/setup/js/sanitize_content.cjs"
1072+ local failed=0
1073+
1074+ # Per spec Section 9.4: Implementations MUST apply these stages in order:
1075+ # S1: Null byte removal (remove \x00 and control chars)
1076+ # S4: HTML tag filtering (remove <script>, <iframe>, on* event handlers)
1077+
1078+ local sanitizer_file=" "
1079+ if [ -f " $core_sanitizer " ]; then
1080+ sanitizer_file=" $core_sanitizer "
1081+ elif [ -f " $fallback_sanitizer " ]; then
1082+ sanitizer_file=" $fallback_sanitizer "
1083+ else
1084+ log_high " INT-002: Sanitization implementation file missing (expected $core_sanitizer )"
1085+ return
1086+ fi
1087+
1088+ # Check S1: Null byte / control character removal (Section 9.4 S1)
1089+ # Spec requires removal of all null bytes (\0, \x00).
1090+ # Implementation may use a control-char range starting at \x00 (e.g., /[\x00-\x08...]/)
1091+ if ! grep -qE ' x00|removeNull|null.*byte|byte.*null' " $sanitizer_file " ; then
1092+ log_high " INT-002: Sanitization pipeline missing null byte removal (Section 9.4 S1)"
1093+ failed=1
1094+ fi
1095+
1096+ # Check S4: HTML tag filtering — <script>, <iframe>, on* event handlers (Section 9.4 S4)
1097+ if ! grep -qE ' script|iframe|on\*|onerror|onclick|event.*handler|dangerous.*attr|strip.*attr' " $sanitizer_file " ; then
1098+ log_high " INT-002: Sanitization pipeline missing HTML tag/event handler filtering (Section 9.4 S4)"
1099+ failed=1
1100+ fi
1101+
1102+ if [ $failed -eq 0 ]; then
1103+ log_pass " INT-002: Sanitization pipeline implements null byte removal (S1) and HTML filtering (S4)"
1104+ fi
1105+ }
1106+ check_sanitization_pipeline
1107+
1108+ # EXEC-001: System Types Processed Last (Section 10.2)
1109+ echo " Running EXEC-001: System Types Processed Last..."
1110+ check_system_types_ordering () {
1111+ local manager_file=" actions/setup/js/safe_output_handler_manager.cjs"
1112+ local failed=0
1113+
1114+ # Per spec Section 10.2: Operations execute in NDJSON order, with system types
1115+ # (noop, missing_tool, missing_data, report_incomplete) processed LAST.
1116+
1117+ if [ ! -f " $manager_file " ]; then
1118+ log_high " EXEC-001: Safe output handler manager missing: $manager_file "
1119+ return
1120+ fi
1121+
1122+ # Check that system types are collected separately (prerequisite for last processing)
1123+ if ! grep -qE " missing_tool.*missing_data.*noop|collect.*missing|system.*type" " $manager_file " ; then
1124+ log_medium " EXEC-001: Handler manager does not appear to separate system types for ordering (Section 10.2)"
1125+ failed=1
1126+ fi
1127+
1128+ # Verify that noop, missing_tool, missing_data, report_incomplete are recognized as a group
1129+ if ! grep -q " report_incomplete" " $manager_file " ; then
1130+ log_medium " EXEC-001: report_incomplete system type not handled in $manager_file (Section 10.2)"
1131+ failed=1
1132+ fi
1133+
1134+ if [ $failed -eq 0 ]; then
1135+ log_pass " EXEC-001: System types (noop, missing_tool, missing_data, report_incomplete) are grouped for last processing"
1136+ fi
1137+ }
1138+ check_system_types_ordering
1139+
1140+ # EXEC-002: Zero Max Limit Disables Type (Section 10.5)
1141+ echo " Running EXEC-002: Zero Max Limit Disables Type..."
1142+ check_zero_max_disables_type () {
1143+ local failed=0
1144+
1145+ # Per spec Section 10.5: When max: 0 is configured for a safe output type,
1146+ # the type MUST be disabled (MCP tool not registered, no config generated).
1147+
1148+ # Check Go compiler: types with max: 0 should not appear in generated config
1149+ if grep -rqE " max.*==.*0|\.Max.*==.*0|maxIsZero|disabledType|skipZeroMax" pkg/workflow/safe_outputs* .go 2> /dev/null; then
1150+ log_pass " EXEC-002: Compiler handles max: 0 type disabling"
1151+ return
1152+ fi
1153+
1154+ # Alternative: check if there are tests validating zero-max disabling
1155+ if grep -rqE " max.*0.*disabled|max.*:.*0|\" max\" .*0" pkg/workflow/safe_outputs* test* .go 2> /dev/null; then
1156+ log_pass " EXEC-002: Tests validate max: 0 type disabling behavior"
1157+ return
1158+ fi
1159+
1160+ # Check if the gateway handler skips tools not present in config (indirectly validates zero-max)
1161+ local gateway=" actions/setup/js/safe_outputs_handlers.cjs"
1162+ if [ -f " $gateway " ]; then
1163+ if grep -qE " toolsConfig|registeredTools|register.*tool|tool.*register" " $gateway " ; then
1164+ log_pass " EXEC-002: Gateway registers tools from config (zero-max types absent from config will not be registered)"
1165+ return
1166+ fi
1167+ fi
1168+
1169+ log_medium " EXEC-002: No explicit evidence that max: 0 disables/unregisters the safe output type (Section 10.5)"
1170+ failed=1
1171+
1172+ if [ $failed -eq 0 ]; then
1173+ log_pass " EXEC-002: max: 0 properly disables safe output type registration"
1174+ fi
1175+ }
1176+ check_zero_max_disables_type
1177+
10231178# Summary
10241179echo " "
10251180echo " =================================================="
0 commit comments