-
Notifications
You must be signed in to change notification settings - Fork 443
Expand file tree
/
Copy pathcheck-safe-outputs-conformance.sh
More file actions
executable file
·1729 lines (1469 loc) · 70.8 KB
/
Copy pathcheck-safe-outputs-conformance.sh
File metadata and controls
executable file
·1729 lines (1469 loc) · 70.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
set +o histexpand
# Safe Outputs Specification Conformance Checker
# This script implements automated checks for the Safe Outputs specification
# Specification: docs/src/content/docs/specs/safe-outputs-specification.md
# Spec Version: 1.24.0 (2026-06-13)
# Script Version: 1.25.0 (2026-06-22)
set -euo pipefail
# Color codes for output
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Counters
CRITICAL_FAILURES=0
HIGH_FAILURES=0
MEDIUM_FAILURES=0
LOW_FAILURES=0
# Logging functions
log_critical() {
echo -e "${RED}[CRITICAL]${NC} $1"
((CRITICAL_FAILURES += 1))
}
log_high() {
echo -e "${RED}[HIGH]${NC} $1"
((HIGH_FAILURES += 1))
}
log_medium() {
echo -e "${YELLOW}[MEDIUM]${NC} $1"
((MEDIUM_FAILURES += 1))
}
log_low() {
echo -e "${BLUE}[LOW]${NC} $1"
((LOW_FAILURES += 1))
}
log_pass() {
echo -e "${GREEN}[PASS]${NC} $1"
}
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
# Change to repo root
cd "$(dirname "$0")/.."
echo "=================================================="
echo "Safe Outputs Specification Conformance Checker"
echo "=================================================="
echo ""
# SEC-001: Privilege Separation Enforcement
echo "Running SEC-001: Privilege Separation Enforcement..."
check_privilege_separation() {
local failed=0
# Find all compiled workflow files
find .github/workflows -name "*.lock.yml" | while read -r workflow; do
# Check if agent job has write permissions
if grep -A 50 "^jobs:" "$workflow" | grep -A 20 "^\s*agent:" | grep -qE "issues:\s*write|pull-requests:\s*write|contents:\s*write"; then
log_critical "SEC-001: Agent job in $workflow has write permissions"
failed=1
fi
done
if [ $failed -eq 0 ]; then
log_pass "SEC-001: All agent jobs properly lack write permissions"
fi
}
check_privilege_separation
# SEC-002: Validation Before API Calls
echo "Running SEC-002: Validation Before API Calls..."
check_validation_ordering() {
local failed=0
for handler in actions/setup/js/*.cjs; do
# Skip test files
[[ "$handler" =~ test ]] && continue
[[ "$handler" =~ parse ]] && continue
[[ "$handler" =~ buffer ]] && continue
# Check if handler has API calls
if grep -q "octokit\." "$handler"; then
# Check if validation appears before API calls
if ! awk '/octokit\./{api_line=NR} /validate|sanitize|enforceLimit/{if(NR<api_line || api_line==0) valid=1} END{exit !valid}' "$handler" 2>/dev/null; then
log_critical "SEC-002: $handler may have API calls before validation"
failed=1
fi
fi
done
if [ $failed -eq 0 ]; then
log_pass "SEC-002: All handlers validate before API calls"
fi
}
check_validation_ordering
# SEC-003: Max Limit Enforcement
echo "Running SEC-003: Max Limit Enforcement..."
check_max_limits() {
local failed=0
for handler in actions/setup/js/*.cjs; do
# Skip test and utility files
[[ "$handler" =~ (test|parse|buffer|factory) ]] && continue
# Only check files that perform GitHub API operations
if ! grep -q "octokit\." "$handler"; then
continue
fi
# Check if handler enforces max limits using any recognized pattern
if ! grep -qE "\.length.*>.*\.max|enforceMaxLimit|checkLimit|max.*exceeded|enforceArrayLimit|tryEnforceArrayLimit|limit_enforcement_helpers" "$handler"; then
log_medium "SEC-003: $handler may not enforce max limits"
failed=1
fi
done
if [ $failed -eq 0 ]; then
log_pass "SEC-003: All handlers enforce max limits"
fi
}
check_max_limits
# SEC-004: Content Sanitization Required
echo "Running SEC-004: Content Sanitization Required..."
check_sanitization() {
local failed=0
for handler in actions/setup/js/*.cjs; do
# Skip test and utility files
[[ "$handler" =~ (test|parse|buffer) ]] && continue
# Skip files with a documented SEC-004 exemption annotation
if grep -q "@safe-outputs-exempt[[:space:]]\\+SEC-004" "$handler"; then
continue
fi
# Check if handler has body/content fields
if grep -q "\"body\"\|body:" "$handler"; then
# Check for sanitization
if ! grep -q "sanitize\|stripHTML\|escapeMarkdown\|cleanContent" "$handler"; then
log_medium "SEC-004: $handler has body field but no sanitization"
failed=1
fi
fi
done
if [ $failed -eq 0 ]; then
log_pass "SEC-004: All handlers properly sanitize content"
fi
}
check_sanitization
# SEC-005: Cross-Repository Validation
echo "Running SEC-005: Cross-Repository Validation..."
check_cross_repo() {
local failed=0
for handler in actions/setup/js/*.cjs; do
# Skip test files
[[ "$handler" =~ test ]] && continue
# Skip files with a documented SEC-005 exemption annotation
if grep -q "@safe-outputs-exempt.*SEC-005" "$handler"; then
continue
fi
# Check if handler supports target-repo
if grep -q "target.*[Rr]epo\|targetRepo" "$handler"; then
# Check for allowlist validation
if ! grep -q "allowed.*[Rr]epos\|validateTargetRepo\|checkAllowedRepo" "$handler"; then
log_high "SEC-005: $handler supports target-repo but lacks allowlist check"
failed=1
fi
fi
done
if [ $failed -eq 0 ]; then
log_pass "SEC-005: All cross-repo handlers validate allowlists"
fi
}
check_cross_repo
# USE-001: Error Code Standardization
echo "Running USE-001: Error Code Standardization..."
check_error_codes() {
local failed=0
for handler in actions/setup/js/*.cjs; do
# Skip test files and non-safe-output modules
[[ "$handler" =~ test ]] && continue
[[ "$handler" =~ (apm_unpack|run_apm_unpack|observability|generate_observability) ]] && continue
# Only check handlers that interact with GitHub via octokit or record safe output operations
if ! grep -qE "octokit\.|safe_output|safeOutput|NDJSON" "$handler"; then
continue
fi
# Check if handler throws errors
if grep -q "throw.*Error\|core\.setFailed" "$handler"; then
# Check for standardized error codes
if ! grep -qE "E[0-9]{3}|ERROR_|ERR_" "$handler"; then
log_low "USE-001: $handler may not use standardized error codes"
failed=1
fi
fi
done
if [ $failed -eq 0 ]; then
log_pass "USE-001: All handlers use standardized error codes"
fi
}
check_error_codes
# USE-002: Footer Attribution Required
echo "Running USE-002: Footer Attribution Required..."
check_footers() {
local failed=0
# Check handlers that create issues/PRs/discussions
for handler in actions/setup/js/{create_issue,create_pull_request,create_discussion,add_comment}.cjs; do
[ ! -f "$handler" ] && continue
# Check if handler adds footers
if ! grep -q "footer\|addFooter\|attribution\|AI generated" "$handler"; then
log_low "USE-002: $handler may not add footer attribution"
failed=1
fi
done
if [ $failed -eq 0 ]; then
log_pass "USE-002: All handlers add footer attribution when configured"
fi
}
check_footers
# USE-003: Staged Mode Preview Format
echo "Running USE-003: Staged Mode Preview Format..."
check_staged_mode() {
local failed=0
for handler in actions/setup/js/*.cjs; do
# Skip test files and non-safe-output modules
[[ "$handler" =~ test ]] && continue
[[ "$handler" =~ (apm_unpack|run_apm_unpack|observability|generate_observability) ]] && continue
# Only check handlers that explicitly reference the safe outputs staged mode env var
if grep -q "GH_AW_SAFE_OUTPUTS_STAGED\|logStagedPreviewInfo\|generateStagedPreview" "$handler"; then
# Check for emoji in preview
if ! grep -q "🎭\|Staged Mode.*Preview\|logStagedPreviewInfo\|generateStagedPreview" "$handler"; then
log_low "USE-003: $handler has staged mode but missing 🎭 emoji"
failed=1
fi
fi
done
if [ $failed -eq 0 ]; then
log_pass "USE-003: All handlers use correct staged mode format"
fi
}
check_staged_mode
# REQ-001: RFC 2119 Keyword Usage
echo "Running REQ-001: RFC 2119 Keyword Usage..."
check_rfc2119() {
local spec_file="docs/src/content/docs/specs/safe-outputs-specification.md"
local failed=0
# Check key sections have RFC 2119 keywords
for section in "Security Architecture" "Configuration Semantics" "Execution Guarantees"; do
if ! grep -A 200 "## .*$section" "$spec_file" 2>/dev/null | grep -q "MUST\|SHALL\|SHOULD\|MAY"; then
log_medium "REQ-001: Section '$section' may lack RFC 2119 keywords"
failed=1
fi
done
if [ $failed -eq 0 ]; then
log_pass "REQ-001: Normative sections use RFC 2119 keywords"
fi
}
check_rfc2119
# REQ-002: Safe Output Type Completeness
echo "Running REQ-002: Safe Output Type Completeness..."
check_type_completeness() {
local spec_file="docs/src/content/docs/specs/safe-outputs-specification.md"
local failed=0
# Extract type names
grep "^#### Type:" "$spec_file" 2>/dev/null | sed 's/^#### Type: //' | head -10 | while read -r type_name; do
sections_found=0
# Check for required sections
for section in "MCP Tool Schema" "Operational Semantics" "Configuration Parameters" "Security Requirements" "Required Permissions"; do
if grep -A 200 "^#### Type: $type_name" "$spec_file" 2>/dev/null | grep -q "**$section**"; then
((sections_found += 1))
fi
done
if [ $sections_found -lt 5 ]; then
log_medium "REQ-002: Type '$type_name' has only $sections_found/5 required sections"
failed=1
fi
done
if [ $failed -eq 0 ]; then
log_pass "REQ-002: All safe output types have complete documentation"
fi
}
check_type_completeness
# REQ-003: Verification Method Specification
echo "Running REQ-003: Verification Method Specification..."
check_verification_methods() {
local spec_file="docs/src/content/docs/specs/safe-outputs-specification.md"
local failed=0
# Check key requirements have verification methods
# Accept both bold (**Verification:**) and italic (*Verification*:) formats
for req in "AR1" "AR2" "AR3" "SP1" "SP2" "SP3"; do
if ! grep -A 30 "\*\*Requirement $req:\|\*\*Property $req:" "$spec_file" 2>/dev/null | grep -qE "\*\*Verification\*\*:|\*Verification\*:|\*\*Formal Definition\*\*:|\*Formal Definition\*:"; then
log_low "REQ-003: Requirement $req may lack verification method"
failed=1
fi
done
if [ $failed -eq 0 ]; then
log_pass "REQ-003: All requirements have verification methods"
fi
}
check_verification_methods
# IMP-001: Handler Registration Completeness
echo "Running IMP-001: Handler Registration Completeness..."
check_handler_registration() {
local failed=0
# Check if standard handlers exist
for type in create_issue add_comment close_issue update_issue add_labels remove_labels; do
handler_file="actions/setup/js/${type}.cjs"
if [ ! -f "$handler_file" ]; then
log_high "IMP-001: Missing handler file $handler_file"
failed=1
fi
done
if [ $failed -eq 0 ]; then
log_pass "IMP-001: All standard handlers are registered"
fi
}
check_handler_registration
# IMP-002: Permission Computation Accuracy
echo "Running IMP-002: Permission Computation Accuracy..."
check_permission_computation() {
# Check if permission computation file exists and is well-formed
if [ -f "pkg/workflow/safe_outputs_permissions.go" ]; then
# Basic check that it defines ComputePermissionsForSafeOutputs
if grep -q "ComputePermissionsForSafeOutputs" "pkg/workflow/safe_outputs_permissions.go"; then
log_pass "IMP-002: Permission computation function exists"
else
log_high "IMP-002: Permission computation function not found"
fi
else
log_high "IMP-002: Permission computation file missing"
fi
}
check_permission_computation
# IMP-003: Schema Validation Consistency
echo "Running IMP-003: Schema Validation Consistency..."
check_schema_consistency() {
local failed=0
# Check if safe outputs config generation file exists with schema functions
if [ -f "pkg/workflow/safe_outputs_config_generation.go" ]; then
# Check for schema generation functions (custom job tool definition generation)
if ! grep -q "generateCustomJobToolDefinition" "pkg/workflow/safe_outputs_config_generation.go"; then
log_medium "IMP-003: Dynamic schema generation function missing"
failed=1
fi
else
log_medium "IMP-003: Safe outputs config generation file missing"
failed=1
fi
# Check if static schemas file exists (embedded JSON)
if [ -f "pkg/workflow/js/safe_outputs_tools.json" ]; then
# Verify it contains MCP tool definitions with inputSchema
if ! grep -q '"inputSchema"' "pkg/workflow/js/safe_outputs_tools.json"; then
log_medium "IMP-003: Static schema definitions missing inputSchema"
failed=1
fi
else
log_medium "IMP-003: Static safe outputs tools schema missing"
failed=1
fi
# Check if safe_outputs_config.go has documentation about schema architecture
if [ -f "pkg/workflow/safe_outputs_config.go" ]; then
if ! grep -q "Schema Generation Architecture" "pkg/workflow/safe_outputs_config.go"; then
log_medium "IMP-003: Schema architecture documentation missing"
failed=1
fi
fi
if [ $failed -eq 0 ]; then
log_pass "IMP-003: Schema generation is implemented"
fi
}
check_schema_consistency
# MCE-001: Tool Description Constraint Disclosure (Section 8.3 MCE2)
echo "Running MCE-001: Tool Description Constraint Disclosure..."
check_mce_constraint_disclosure() {
local tools_json="pkg/workflow/js/safe_outputs_tools.json"
local failed=0
if [ ! -f "$tools_json" ]; then
log_high "MCE-001: Tool definitions file missing: $tools_json"
return
fi
# Per spec Section 8.3 MCE2: add_comment MUST surface its constraint limits in description
# Required: 65536 char limit, 10 mentions, 50 links (checks the combined tool JSON file)
if ! grep -iE "65536" "$tools_json" > /dev/null 2>&1; then
log_medium "MCE-001: add_comment tool description may be missing 65536 character limit"
failed=1
fi
if ! grep -iE "10 mention" "$tools_json" > /dev/null 2>&1; then
log_medium "MCE-001: add_comment tool description may be missing 10 mention limit"
failed=1
fi
if ! grep -iE "50 link" "$tools_json" > /dev/null 2>&1; then
log_medium "MCE-001: add_comment tool description may be missing 50 link limit"
failed=1
fi
# Verify add_comment description contains CONSTRAINTS or IMPORTANT keyword
if ! grep -A 5 '"add_comment"' "$tools_json" | grep -qE "CONSTRAINTS|IMPORTANT.*constraint|validation constraint"; then
log_medium "MCE-001: add_comment tool description missing required CONSTRAINTS/IMPORTANT disclosure"
failed=1
fi
if [ $failed -eq 0 ]; then
log_pass "MCE-001: Tool descriptions properly disclose enforcement constraints"
fi
}
check_mce_constraint_disclosure
# MCE-002: Dual Enforcement Pattern (Section 8.3 MCE4)
echo "Running MCE-002: Dual Enforcement Pattern..."
check_mce_dual_enforcement() {
local failed=0
local helpers_file="actions/setup/js/comment_limit_helpers.cjs"
# Per spec Section 8.3 MCE4: constraints must be enforced at both MCP invocation
# time and safe output processing time
# Check constraint helper module exists
if [ ! -f "$helpers_file" ]; then
log_high "MCE-002: Constraint helper module missing: $helpers_file"
failed=1
return
fi
# Check that both the MCP gateway handler (records operations) and the safe output
# processor (add_comment.cjs - executes API calls) import/use the constraint helpers.
# Per spec MCE4: dual enforcement must exist at both invocation and processing time.
local gateway_handler="actions/setup/js/safe_outputs_handlers.cjs"
local add_comment_handler="actions/setup/js/add_comment.cjs"
for handler in "$gateway_handler" "$add_comment_handler"; do
if [ ! -f "$handler" ]; then
log_medium "MCE-002: Expected handler file missing: $handler"
failed=1
continue
fi
if ! grep -q "comment_limit_helpers\|enforceCommentLimits" "$handler"; then
log_medium "MCE-002: $handler does not enforce comment constraints (dual enforcement pattern)"
failed=1
fi
done
if [ $failed -eq 0 ]; then
log_pass "MCE-002: Dual enforcement pattern implemented in both gateway and processor"
fi
}
check_mce_dual_enforcement
# CI-001: Cache Memory Integrity Scripts Exist (Section 11 CI6, CI10)
echo "Running CI-001: Cache Memory Integrity Scripts Exist..."
check_cache_memory_scripts() {
local failed=0
# Per spec Section 11 CI6 and CI10: setup and commit scripts must exist
local setup_script="actions/setup/sh/setup_cache_memory_git.sh"
local commit_script="actions/setup/sh/commit_cache_memory_git.sh"
for script in "$setup_script" "$commit_script"; do
if [ ! -f "$script" ]; then
log_high "CI-001: Required cache memory integrity script missing: $script"
failed=1
fi
done
if [ $failed -eq 0 ]; then
log_pass "CI-001: Cache memory integrity scripts exist"
fi
}
check_cache_memory_scripts
# CI-002: Cache Memory Integrity Branch Support (Section 11.2, CI7, CI8)
echo "Running CI-002: Cache Memory Integrity Branch Support..."
check_cache_integrity_branches() {
local setup_script="actions/setup/sh/setup_cache_memory_git.sh"
local commit_script="actions/setup/sh/commit_cache_memory_git.sh"
local failed=0
if [ ! -f "$setup_script" ]; then
log_medium "CI-002: Setup script missing — skipping integrity branch check"
return
fi
# Per spec Section 11.2: all four integrity levels must be supported (merged > approved > unapproved > none)
for level in merged approved unapproved none; do
if ! grep -q "\"$level\"\|'$level'" "$setup_script"; then
log_high "CI-002: Integrity level '$level' not found in setup script"
failed=1
fi
done
# Per spec CI8: merge-down from higher-integrity branches must be implemented
if ! grep -q "merge\|git merge" "$setup_script"; then
log_high "CI-002: Setup script missing merge-down implementation (CI8)"
failed=1
fi
# Per spec CI9: merge failure must abort and exit with non-zero status
if ! grep -qE "merge.*abort|abort.*merge|exit.*\$|exit [0-9]" "$setup_script"; then
log_high "CI-002: Setup script missing merge failure abort/exit handling (CI9)"
failed=1
fi
# Per spec CI11: commit script must invoke git gc --auto for compaction
if [ -f "$commit_script" ]; then
if ! grep -q "git gc" "$commit_script"; then
log_medium "CI-002: Commit script missing 'git gc --auto' (CI11 repository compaction)"
failed=1
fi
fi
# Per spec CI12: commit script must handle missing .git gracefully
if [ -f "$commit_script" ]; then
if ! grep -q '\.git\|no.*git\|skip.*git' "$commit_script"; then
log_medium "CI-002: Commit script may not handle missing .git directory (CI12)"
failed=1
fi
fi
if [ $failed -eq 0 ]; then
log_pass "CI-002: Cache memory integrity branching properly implemented"
fi
}
check_cache_integrity_branches
# MCE-003: Constraint Limit Consistency (Section 8.3 MCE5)
echo "Running MCE-003: Constraint Limit Consistency..."
check_mce_constraint_consistency() {
local tools_json="pkg/workflow/js/safe_outputs_tools.json"
local helpers_file="actions/setup/js/comment_limit_helpers.cjs"
local failed=0
if [ ! -f "$helpers_file" ]; then
log_high "MCE-003: Constraint helper module missing: $helpers_file"
return
fi
if [ ! -f "$tools_json" ]; then
log_high "MCE-003: Tool definitions file missing: $tools_json"
return
fi
# Per spec Section 8.3 MCE5: limits in tool descriptions MUST match enforcement code.
# Extract the declared limits from comment_limit_helpers.cjs and verify they appear
# verbatim in the tools JSON, ensuring both layers agree on the constraint values.
# Check body length limit (65536) appears in both files
if ! grep -q "65536" "$helpers_file"; then
log_high "MCE-003: MAX_COMMENT_LENGTH (65536) not found in $helpers_file"
failed=1
fi
if ! grep -q "65536" "$tools_json"; then
log_high "MCE-003: 65536 character limit not found in $tools_json"
failed=1
fi
# Check mention limit (10) appears in both files
if ! grep -qE "MAX_MENTIONS\s*=\s*10|max.*mention.*10|10.*mention" "$helpers_file"; then
log_high "MCE-003: MAX_MENTIONS (10) not declared in $helpers_file"
failed=1
fi
if ! grep -qiE "10 mention|mention.*10" "$tools_json"; then
log_high "MCE-003: 10-mention limit not found in $tools_json"
failed=1
fi
# Check link limit (50) appears in both files
if ! grep -qE "MAX_LINKS\s*=\s*50|max.*link.*50|50.*link" "$helpers_file"; then
log_high "MCE-003: MAX_LINKS (50) not declared in $helpers_file"
failed=1
fi
if ! grep -qiE "50 link|link.*50" "$tools_json"; then
log_high "MCE-003: 50-link limit not found in $tools_json"
failed=1
fi
if [ $failed -eq 0 ]; then
log_pass "MCE-003: Constraint limits are consistent between tool descriptions and enforcement code"
fi
}
check_mce_constraint_consistency
# CI-003: Policy Hash and Nopolicy Sentinel (Section 11.4 CI3, CI4)
echo "Running CI-003: Policy Hash and Nopolicy Sentinel..."
check_policy_hash_implementation() {
local cache_integrity_file="pkg/workflow/cache_integrity.go"
local failed=0
# Per spec Section 11.4 CI3: policy hash MUST be SHA-256, first 8 chars of lowercase hex
# Per spec Section 11.4 CI4: workflows without policy MUST use "nopolicy" sentinel
if [ ! -f "$cache_integrity_file" ]; then
log_high "CI-003: Cache integrity implementation missing: $cache_integrity_file"
return
fi
# Check SHA-256 is used for policy hash computation (CI3)
if ! grep -q "sha256\|crypto/sha256" "$cache_integrity_file"; then
log_high "CI-003: SHA-256 not used for policy hash computation (CI3)"
failed=1
fi
# Check nopolicy sentinel constant exists (CI4)
if ! grep -q "nopolicy\|noPolicySentinel" "$cache_integrity_file"; then
log_high "CI-003: 'nopolicy' sentinel not found in cache integrity implementation (CI4)"
failed=1
fi
# Check that the 8-character prefix is taken (CI3: first 8 chars of lowercase hex)
if ! grep -qE "\[:8\]|first.*8|8.*char|hex\[:8\]" "$cache_integrity_file"; then
log_medium "CI-003: 8-character hash prefix truncation not evident in $cache_integrity_file (CI3)"
failed=1
fi
if [ $failed -eq 0 ]; then
log_pass "CI-003: Policy hash uses SHA-256 with nopolicy sentinel as required"
fi
}
check_policy_hash_implementation
# CI-004: .git Directory Exclusion from Cache Memory Validation (Section 11.5 CI5)
echo "Running CI-004: .git Directory Exclusion from Validation..."
check_git_dir_exclusion() {
local setup_script="actions/setup/sh/setup_cache_memory_git.sh"
local failed=0
# Per spec Section 11.5 CI5: file validation steps MUST skip the .git directory.
# The .git directory contains binary/extension-less files not managed by the agent.
if [ ! -f "$setup_script" ]; then
log_medium "CI-004: Setup script missing — skipping .git exclusion check"
return
fi
# Check that .git is referenced in the context of exclusion or skip logic
if ! grep -qE "\.git|git_dir|skip.*\.git|exclude.*git|prune.*git" "$setup_script"; then
log_medium "CI-004: Setup script does not reference .git exclusion (CI5)"
failed=1
fi
# Check compiled workflow lock files: cache-memory file validation should skip .git
if find .github/workflows -name "*.lock.yml" | xargs grep -l "cache-memory\|GH_AW_CACHE_MEMORY" 2>/dev/null | \
xargs grep -l "validate\|allowed.*ext\|file.*check" 2>/dev/null | \
xargs grep -qv "\.git\|skip.*git" 2>/dev/null; then
log_low "CI-004: Some cache-memory workflow lock files may not exclude .git in validation (CI5)"
# Not failing here — informational only as implementation details vary
fi
if [ $failed -eq 0 ]; then
log_pass "CI-004: .git directory exclusion from validation is present"
fi
}
check_git_dir_exclusion
# CI-005: Integrity-Scoped Cache Keys (Section 11.3 CI1)
echo "Running CI-005: Integrity-Scoped Cache Keys..."
check_integrity_scoped_keys() {
local failed=0
# Per spec Section 11.3 CI1: All cache-memory keys MUST include the integrity level
# and policy hash as prefixes in the format: memory-{integrityLevel}-{policyHash}-...
local cache_lock_files
cache_lock_files=$(find .github/workflows -name "*.lock.yml" -exec grep -l "GH_AW_CACHE_MEMORY\|cache-memory" {} \; 2>/dev/null)
if [ -z "$cache_lock_files" ]; then
log_pass "CI-005: No cache-memory workflows found — nothing to check"
return
fi
while IFS= read -r workflow; do
# Extract cache keys from the workflow
# Valid format: memory-{integrityLevel}-{policyHash}-...
# integrityLevel must be one of: merged, approved, unapproved, none
# policyHash must be 8-char hex or the sentinel "nopolicy"
while IFS= read -r key_line; do
key=$(echo "$key_line" | sed 's/.*key:\s*//')
if [[ "$key" =~ ^memory- ]]; then
if ! echo "$key" | grep -qE "^memory-(merged|approved|unapproved|none)-(nopolicy|[0-9a-f]{8})-"; then
log_high "CI-005: Cache key in $workflow does not follow integrity-scoped format (CI1): $key"
failed=1
fi
fi
done < <(grep "key: memory-" "$workflow" 2>/dev/null)
done <<< "$cache_lock_files"
if [ $failed -eq 0 ]; then
log_pass "CI-005: All cache-memory keys use the integrity-scoped format (CI1)"
fi
}
check_integrity_scoped_keys
# CI-006: Restore Key Cascade (Section 11.3 CI2)
echo "Running CI-006: Restore Key Cascade..."
check_restore_key_cascade() {
local failed=0
# Per spec Section 11.3 CI2: Restore keys MUST use the same integrity-scoped prefix
# so that a partial key match never crosses integrity level boundaries.
# The restore-keys pattern must not contain a run_id (to allow matching prior runs).
local cache_lock_files
cache_lock_files=$(find .github/workflows -name "*.lock.yml" -exec grep -l "GH_AW_CACHE_MEMORY\|cache-memory" {} \; 2>/dev/null)
if [ -z "$cache_lock_files" ]; then
log_pass "CI-006: No cache-memory workflows found — nothing to check"
return
fi
while IFS= read -r workflow; do
# Check each restore-key entry that starts with "memory-"
while IFS= read -r restore_key; do
key=$(echo "$restore_key" | sed 's/^\s*//')
if [[ "$key" =~ ^memory- ]]; then
# restore-keys should include the integrity level and policy hash prefix
# but must NOT include the run_id (to match prior runs of same workflow)
if ! echo "$key" | grep -qE "^memory-(merged|approved|unapproved|none)-(nopolicy|[0-9a-f]{8})-"; then
# Allow the legacy fallback "memory-" entry documented in spec
if [ "$key" != "memory-" ]; then
log_high "CI-006: Restore key in $workflow does not use integrity-scoped prefix (CI2): $key"
failed=1
fi
fi
# Restore keys must NOT include github.run_id (they are prefix-only)
if echo "$key" | grep -q "run_id"; then
log_medium "CI-006: Restore key in $workflow includes run_id — should be prefix-only for cascade (CI2): $key"
failed=1
fi
fi
done < <(awk '/restore-keys:/,/^[^|]/' "$workflow" 2>/dev/null | grep "memory-")
done <<< "$cache_lock_files"
if [ $failed -eq 0 ]; then
log_pass "CI-006: All cache-memory restore keys use the integrity-scoped prefix (CI2)"
fi
}
check_restore_key_cascade
# MCE-004: Early Validation at MCP Invocation (Section 8.3 MCE1)
echo "Running MCE-004: Early Validation at MCP Invocation..."
check_mce_early_validation() {
local gateway_handler="actions/setup/js/safe_outputs_handlers.cjs"
local failed=0
# Per spec Section 8.3 MCE1: MCP servers MUST enforce operational constraints during
# tool invocation (Phase 4) rather than deferring all validation to safe output
# processing (Phase 6). This provides immediate feedback to the LLM.
if [ ! -f "$gateway_handler" ]; then
log_high "MCE-004: MCP gateway handler missing: $gateway_handler"
return
fi
# Check that constraint validation (enforceCommentLimits or equivalent) is called
# before the operation is appended to safe outputs (appendSafeOutput)
if ! grep -q "enforceCommentLimits\|enforceConstraints\|validateEarly" "$gateway_handler"; then
log_high "MCE-004: MCP gateway handler does not call early constraint validation (MCE1)"
failed=1
fi
# Verify the handler references MCE1 requirement in documentation
if ! grep -q "MCE1\|Early Validation\|tool invocation" "$gateway_handler"; then
log_medium "MCE-004: MCE1 early validation pattern not documented in $gateway_handler"
failed=1
fi
# Ensure validation occurs before recording (appendSafeOutput / recordOperation)
# by checking that enforceCommentLimits appears before appendSafeOutput in the file
enforce_line=$(grep -n "enforceCommentLimits" "$gateway_handler" | head -1 | cut -d: -f1)
append_line=$(grep -n "appendSafeOutput" "$gateway_handler" | head -1 | cut -d: -f1)
if [ -n "$enforce_line" ] && [ -n "$append_line" ]; then
if [ "$enforce_line" -gt "$append_line" ]; then
log_critical "MCE-004: enforceCommentLimits appears AFTER appendSafeOutput — validation is not early (MCE1)"
failed=1
fi
fi
if [ $failed -eq 0 ]; then
log_pass "MCE-004: MCP gateway enforces constraints early at tool invocation (MCE1)"
fi
}
check_mce_early_validation
# MCE-005: Actionable Error Responses (Section 8.3 MCE3)
echo "Running MCE-005: Actionable Error Responses..."
check_mce_actionable_errors() {
local helpers_file="actions/setup/js/comment_limit_helpers.cjs"
local gateway_handler="actions/setup/js/safe_outputs_handlers.cjs"
local failed=0
# Per spec Section 8.3 MCE3: When constraints are violated, MCP servers MUST return
# error responses that:
# 1. Identify the violated constraint with specific name and limit
# 2. Report the actual value that triggered the violation
# 3. Provide remediation guidance on how to correct the issue
# 4. Use standard error codes (E006-E008 for add_comment limits)
if [ ! -f "$helpers_file" ]; then
log_high "MCE-005: Constraint helper module missing: $helpers_file"
return
fi
# Check that error messages identify the constraint name and limit (requirement 1)
if ! grep -qE "E006.*length|E007.*mention|E008.*link" "$helpers_file"; then
log_medium "MCE-005: Error messages do not clearly identify constraint name in $helpers_file (MCE3 req 1)"
failed=1
fi
# Check that error messages report the actual violating value (requirement 2)
# Look for patterns like "got ${body.length}" or "contains ${mentions}"
if ! grep -qE "got \\\${|contains \\\${|\\.length\}" "$helpers_file"; then
log_medium "MCE-005: Error messages do not report the actual violating value in $helpers_file (MCE3 req 2)"
failed=1
fi
# Check that error responses include remediation guidance (requirement 3)
# Either via a structured data.guidance field or guidance-oriented language in errors
if ! grep -qE "guidance|reduce|Reduce|consider|Consider|fewer|lower" "$helpers_file"; then
log_low "MCE-005: Error responses in $helpers_file may lack remediation guidance (MCE3 req 3)"
failed=1
fi
# Check that standard error codes are used (requirement 4)
if ! grep -qE "E006|E007|E008" "$helpers_file"; then
log_medium "MCE-005: Standard error codes E006-E008 not used in $helpers_file (MCE3 req 4)"
failed=1
fi
# Check gateway handler re-throws with standard -32602 JSON-RPC error code
if [ -f "$gateway_handler" ]; then
if ! grep -q "\-32602" "$gateway_handler"; then
log_medium "MCE-005: MCP gateway handler does not return -32602 JSON-RPC error code for constraint violations (MCE3)"
failed=1
fi
fi
if [ $failed -eq 0 ]; then
log_pass "MCE-005: Error responses include constraint identification, actual values, and standard codes (MCE3)"
fi
}
check_mce_actionable_errors
# MCE-006: JSON-RPC Core Server Error Code Validity and Plain Object Error Handling (Section 8.2)
echo "Running MCE-006: JSON-RPC Core Server Error Code Validity..."
check_mce_core_error_handling() {
local core_file="actions/setup/js/mcp_server_core.cjs"
local failed=0
# Per spec Section 8.2 (JSON-RPC 2.0 compliance): The MCP server MUST return valid
# JSON-RPC error codes (negative integers) in all error responses. Positive integers
# such as process exit codes (1, 2, etc.) are NOT valid JSON-RPC error codes and MUST
# NOT be used, as they produce non-conformant responses (e.g. "code=1").
# The handleMessage function MUST also properly serialize error messages from thrown
# plain objects (non-Error instances) to avoid '[object Object]' in error responses.
if [ ! -f "$core_file" ]; then
log_high "MCE-006: MCP server core file missing: $core_file"
return
fi
# Check that the error code is validated as a negative integer before use.
# The guard pattern (e.code < 0) prevents positive subprocess exit codes from
# being forwarded as JSON-RPC error codes.
if ! grep -qE "e\.code < 0|err\.code < 0|code.*<.*0" "$core_file"; then
log_critical "MCE-006: MCP server core does not guard against non-negative JSON-RPC error codes — non-conformant responses possible (Section 8.2)"
failed=1
fi
# Check that error messages from thrown plain objects are serialized with String()
# to prevent '[object Object]' appearing as the error message when non-Error
# instances are thrown by handler code.
if ! grep -qE 'String\(e\.message\)|String\(err\.message\)' "$core_file"; then
log_high "MCE-006: MCP server core does not use String() for error message serialization — risk of '[object Object]' in responses (Section 8.2)"
failed=1
fi
if [ $failed -eq 0 ]; then
log_pass "MCE-006: MCP server core enforces valid JSON-RPC error codes and proper error message serialization (Section 8.2)"
fi
}
check_mce_core_error_handling
# TYPE-001: merge_pull_request Handler Existence and Default Branch Protection (Section 7.3, v1.17.0)
echo "Running TYPE-001: merge_pull_request Handler Existence and Default Branch Protection..."
check_merge_pull_request_handler() {
local handler="actions/setup/js/merge_pull_request.cjs"
local failed=0
# Per spec Section 7.3: merge_pull_request handler must exist
if [ ! -f "$handler" ]; then
log_high "TYPE-001: merge_pull_request handler missing: $handler"
return
fi
# Per spec Section 7.3: Merge to the repository default branch MUST be refused
if ! grep -q "isDefault" "$handler"; then
log_critical "TYPE-001: merge_pull_request handler does not check isDefault — default branch protection missing"
failed=1
fi
# Per spec Section 7.3: Policy gates must be enforced (required checks, review decision)
if ! grep -qE "requiredChecks|review_decision|required_labels|allowed_labels" "$handler"; then
log_high "TYPE-001: merge_pull_request handler missing policy gate checks"
failed=1
fi
# Per spec Section 7.3: mergeability check must be present
if ! grep -q "mergeable" "$handler"; then
log_high "TYPE-001: merge_pull_request handler does not verify mergeability"
failed=1
fi
if [ $failed -eq 0 ]; then
log_pass "TYPE-001: merge_pull_request handler exists with default branch protection and policy gates"
fi
}
check_merge_pull_request_handler
# TYPE-002: comment_memory Memory ID Validation (Section 7.3, v1.18.0)
echo "Running TYPE-002: comment_memory Memory ID Validation..."
check_comment_memory_validation() {
local handler="actions/setup/js/comment_memory.cjs"
local helpers="actions/setup/js/comment_memory_helpers.cjs"
local failed=0
# Per spec Section 7.3: comment_memory handler must exist
if [ ! -f "$handler" ]; then
log_high "TYPE-002: comment_memory handler missing: $handler"
return
fi
# Per spec Section 7.3: memory_id MUST be validated as [A-Za-z0-9_-]+
if ! grep -qE "\[A-Za-z0-9_-\]" "$handler" "$helpers" 2>/dev/null; then
log_critical "TYPE-002: comment_memory does not validate memory_id with [A-Za-z0-9_-]+ pattern — path traversal risk"
failed=1
fi
# Per spec Section 7.3: Managed comment scan MUST be bounded by a maximum page limit
if ! grep -qE "MAX_SCAN_PAGES|maxScanPages|scan.*limit|COMMENT_MEMORY_MAX_SCAN" "$handler"; then
log_high "TYPE-002: comment_memory scan is not bounded by a maximum page limit"
failed=1
fi
# Per spec Section 7.3: Body content MUST undergo sanitization before upsert
if ! grep -qE "sanitize|validateBody|enforceComment" "$handler"; then
log_high "TYPE-002: comment_memory body content does not appear to be sanitized before upsert"
failed=1
fi