@contrast/agent 4.30.0 → 4.31.0
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/lib/assess/policy/propagators.json +4 -0
- package/lib/assess/policy/rules.json +44 -44
- package/lib/assess/policy/signatures.json +5 -0
- package/lib/assess/propagators/fastify-static/allowed-path.js +1 -1
- package/lib/assess/propagators/serve-static.js +109 -0
- package/lib/constants.js +1 -0
- package/lib/contrast.js +3 -1
- package/lib/core/async-storage/index.js +1 -0
- package/lib/core/config/options.js +8 -0
- package/lib/util/config-diagnostics-utils.js +12 -1
- package/package.json +2 -1
|
@@ -612,7 +612,7 @@
|
|
|
612
612
|
"args": [
|
|
613
613
|
{
|
|
614
614
|
"index": 0,
|
|
615
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
615
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
616
616
|
"requiredTags": ["untrusted"]
|
|
617
617
|
}
|
|
618
618
|
]
|
|
@@ -626,7 +626,7 @@
|
|
|
626
626
|
"args": [
|
|
627
627
|
{
|
|
628
628
|
"index": 0,
|
|
629
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
629
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
630
630
|
"requiredTags": ["untrusted"]
|
|
631
631
|
}
|
|
632
632
|
]
|
|
@@ -640,12 +640,12 @@
|
|
|
640
640
|
"args": [
|
|
641
641
|
{
|
|
642
642
|
"index": 0,
|
|
643
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
643
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
644
644
|
"requiredTags": ["untrusted"]
|
|
645
645
|
},
|
|
646
646
|
{
|
|
647
647
|
"index": 1,
|
|
648
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
648
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
649
649
|
"requiredTags": ["untrusted"]
|
|
650
650
|
}
|
|
651
651
|
]
|
|
@@ -659,12 +659,12 @@
|
|
|
659
659
|
"args": [
|
|
660
660
|
{
|
|
661
661
|
"index": 0,
|
|
662
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
662
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
663
663
|
"requiredTags": ["untrusted"]
|
|
664
664
|
},
|
|
665
665
|
{
|
|
666
666
|
"index": 1,
|
|
667
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
667
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
668
668
|
"requiredTags": ["untrusted"]
|
|
669
669
|
}
|
|
670
670
|
]
|
|
@@ -678,7 +678,7 @@
|
|
|
678
678
|
"args": [
|
|
679
679
|
{
|
|
680
680
|
"index": 0,
|
|
681
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
681
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
682
682
|
"requiredTags": ["untrusted"]
|
|
683
683
|
}
|
|
684
684
|
]
|
|
@@ -693,7 +693,7 @@
|
|
|
693
693
|
"args": [
|
|
694
694
|
{
|
|
695
695
|
"index": 0,
|
|
696
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
696
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
697
697
|
"requiredTags": ["untrusted"]
|
|
698
698
|
}
|
|
699
699
|
]
|
|
@@ -707,7 +707,7 @@
|
|
|
707
707
|
"args": [
|
|
708
708
|
{
|
|
709
709
|
"index": 0,
|
|
710
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
710
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
711
711
|
"requiredTags": ["untrusted"]
|
|
712
712
|
}
|
|
713
713
|
]
|
|
@@ -722,7 +722,7 @@
|
|
|
722
722
|
"args": [
|
|
723
723
|
{
|
|
724
724
|
"index": 0,
|
|
725
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
725
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
726
726
|
"requiredTags": ["untrusted"]
|
|
727
727
|
}
|
|
728
728
|
]
|
|
@@ -736,7 +736,7 @@
|
|
|
736
736
|
"args": [
|
|
737
737
|
{
|
|
738
738
|
"index": 0,
|
|
739
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
739
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
740
740
|
"requiredTags": ["untrusted"]
|
|
741
741
|
}
|
|
742
742
|
]
|
|
@@ -750,7 +750,7 @@
|
|
|
750
750
|
"args": [
|
|
751
751
|
{
|
|
752
752
|
"index": 0,
|
|
753
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
753
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
754
754
|
"requiredTags": ["untrusted"]
|
|
755
755
|
}
|
|
756
756
|
]
|
|
@@ -764,7 +764,7 @@
|
|
|
764
764
|
"args": [
|
|
765
765
|
{
|
|
766
766
|
"index": 0,
|
|
767
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
767
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
768
768
|
"requiredTags": ["untrusted"]
|
|
769
769
|
}
|
|
770
770
|
]
|
|
@@ -779,7 +779,7 @@
|
|
|
779
779
|
"args": [
|
|
780
780
|
{
|
|
781
781
|
"index": 0,
|
|
782
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
782
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
783
783
|
"requiredTags": ["untrusted"]
|
|
784
784
|
}
|
|
785
785
|
]
|
|
@@ -793,7 +793,7 @@
|
|
|
793
793
|
"args": [
|
|
794
794
|
{
|
|
795
795
|
"index": 0,
|
|
796
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
796
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
797
797
|
"requiredTags": ["untrusted"]
|
|
798
798
|
}
|
|
799
799
|
]
|
|
@@ -808,7 +808,7 @@
|
|
|
808
808
|
"args": [
|
|
809
809
|
{
|
|
810
810
|
"index": 0,
|
|
811
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
811
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
812
812
|
"requiredTags": ["untrusted"]
|
|
813
813
|
}
|
|
814
814
|
]
|
|
@@ -822,7 +822,7 @@
|
|
|
822
822
|
"args": [
|
|
823
823
|
{
|
|
824
824
|
"index": 0,
|
|
825
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
825
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
826
826
|
"requiredTags": ["untrusted"]
|
|
827
827
|
}
|
|
828
828
|
]
|
|
@@ -837,7 +837,7 @@
|
|
|
837
837
|
"args": [
|
|
838
838
|
{
|
|
839
839
|
"index": 0,
|
|
840
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
840
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
841
841
|
"requiredTags": ["untrusted"]
|
|
842
842
|
}
|
|
843
843
|
]
|
|
@@ -851,12 +851,12 @@
|
|
|
851
851
|
"args": [
|
|
852
852
|
{
|
|
853
853
|
"index": 0,
|
|
854
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
854
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
855
855
|
"requiredTags": ["untrusted"]
|
|
856
856
|
},
|
|
857
857
|
{
|
|
858
858
|
"index": 1,
|
|
859
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
859
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
860
860
|
"requiredTags": ["untrusted"]
|
|
861
861
|
}
|
|
862
862
|
]
|
|
@@ -871,12 +871,12 @@
|
|
|
871
871
|
"args": [
|
|
872
872
|
{
|
|
873
873
|
"index": 0,
|
|
874
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
874
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
875
875
|
"requiredTags": ["untrusted"]
|
|
876
876
|
},
|
|
877
877
|
{
|
|
878
878
|
"index": 1,
|
|
879
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
879
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
880
880
|
"requiredTags": ["untrusted"]
|
|
881
881
|
}
|
|
882
882
|
]
|
|
@@ -890,7 +890,7 @@
|
|
|
890
890
|
"args": [
|
|
891
891
|
{
|
|
892
892
|
"index": 0,
|
|
893
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
893
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
894
894
|
"requiredTags": ["untrusted"]
|
|
895
895
|
}
|
|
896
896
|
]
|
|
@@ -905,7 +905,7 @@
|
|
|
905
905
|
"args": [
|
|
906
906
|
{
|
|
907
907
|
"index": 0,
|
|
908
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
908
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
909
909
|
"requiredTags": ["untrusted"]
|
|
910
910
|
}
|
|
911
911
|
]
|
|
@@ -919,7 +919,7 @@
|
|
|
919
919
|
"args": [
|
|
920
920
|
{
|
|
921
921
|
"index": 0,
|
|
922
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
922
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
923
923
|
"requiredTags": ["untrusted"]
|
|
924
924
|
}
|
|
925
925
|
]
|
|
@@ -934,7 +934,7 @@
|
|
|
934
934
|
"args": [
|
|
935
935
|
{
|
|
936
936
|
"index": 0,
|
|
937
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
937
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
938
938
|
"requiredTags": ["untrusted"]
|
|
939
939
|
}
|
|
940
940
|
]
|
|
@@ -948,7 +948,7 @@
|
|
|
948
948
|
"args": [
|
|
949
949
|
{
|
|
950
950
|
"index": 0,
|
|
951
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
951
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
952
952
|
"requiredTags": ["untrusted"]
|
|
953
953
|
}
|
|
954
954
|
]
|
|
@@ -963,7 +963,7 @@
|
|
|
963
963
|
"args": [
|
|
964
964
|
{
|
|
965
965
|
"index": 0,
|
|
966
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
966
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
967
967
|
"requiredTags": ["untrusted"]
|
|
968
968
|
}
|
|
969
969
|
]
|
|
@@ -977,7 +977,7 @@
|
|
|
977
977
|
"args": [
|
|
978
978
|
{
|
|
979
979
|
"index": 0,
|
|
980
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
980
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
981
981
|
"requiredTags": ["untrusted"]
|
|
982
982
|
}
|
|
983
983
|
]
|
|
@@ -992,7 +992,7 @@
|
|
|
992
992
|
"args": [
|
|
993
993
|
{
|
|
994
994
|
"index": 0,
|
|
995
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
995
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
996
996
|
"requiredTags": ["untrusted"]
|
|
997
997
|
}
|
|
998
998
|
]
|
|
@@ -1006,7 +1006,7 @@
|
|
|
1006
1006
|
"args": [
|
|
1007
1007
|
{
|
|
1008
1008
|
"index": 0,
|
|
1009
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1009
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1010
1010
|
"requiredTags": ["untrusted"]
|
|
1011
1011
|
}
|
|
1012
1012
|
]
|
|
@@ -1021,7 +1021,7 @@
|
|
|
1021
1021
|
"args": [
|
|
1022
1022
|
{
|
|
1023
1023
|
"index": 0,
|
|
1024
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1024
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1025
1025
|
"requiredTags": ["untrusted"]
|
|
1026
1026
|
}
|
|
1027
1027
|
]
|
|
@@ -1035,12 +1035,12 @@
|
|
|
1035
1035
|
"args": [
|
|
1036
1036
|
{
|
|
1037
1037
|
"index": 0,
|
|
1038
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1038
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1039
1039
|
"requiredTags": ["untrusted"]
|
|
1040
1040
|
},
|
|
1041
1041
|
{
|
|
1042
1042
|
"index": 1,
|
|
1043
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1043
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1044
1044
|
"requiredTags": ["untrusted"]
|
|
1045
1045
|
}
|
|
1046
1046
|
]
|
|
@@ -1055,12 +1055,12 @@
|
|
|
1055
1055
|
"args": [
|
|
1056
1056
|
{
|
|
1057
1057
|
"index": 0,
|
|
1058
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1058
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1059
1059
|
"requiredTags": ["untrusted"]
|
|
1060
1060
|
},
|
|
1061
1061
|
{
|
|
1062
1062
|
"index": 1,
|
|
1063
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1063
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1064
1064
|
"requiredTags": ["untrusted"]
|
|
1065
1065
|
}
|
|
1066
1066
|
]
|
|
@@ -1074,7 +1074,7 @@
|
|
|
1074
1074
|
"args": [
|
|
1075
1075
|
{
|
|
1076
1076
|
"index": 0,
|
|
1077
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1077
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1078
1078
|
"requiredTags": ["untrusted"]
|
|
1079
1079
|
}
|
|
1080
1080
|
]
|
|
@@ -1089,7 +1089,7 @@
|
|
|
1089
1089
|
"args": [
|
|
1090
1090
|
{
|
|
1091
1091
|
"index": 0,
|
|
1092
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1092
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1093
1093
|
"requiredTags": ["untrusted"]
|
|
1094
1094
|
}
|
|
1095
1095
|
]
|
|
@@ -1103,7 +1103,7 @@
|
|
|
1103
1103
|
"args": [
|
|
1104
1104
|
{
|
|
1105
1105
|
"index": 0,
|
|
1106
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1106
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1107
1107
|
"requiredTags": ["untrusted"]
|
|
1108
1108
|
}
|
|
1109
1109
|
]
|
|
@@ -1118,7 +1118,7 @@
|
|
|
1118
1118
|
"args": [
|
|
1119
1119
|
{
|
|
1120
1120
|
"index": 0,
|
|
1121
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1121
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1122
1122
|
"requiredTags": ["untrusted"]
|
|
1123
1123
|
}
|
|
1124
1124
|
]
|
|
@@ -1132,7 +1132,7 @@
|
|
|
1132
1132
|
"args": [
|
|
1133
1133
|
{
|
|
1134
1134
|
"index": 0,
|
|
1135
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1135
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1136
1136
|
"requiredTags": ["untrusted"]
|
|
1137
1137
|
}
|
|
1138
1138
|
]
|
|
@@ -1147,7 +1147,7 @@
|
|
|
1147
1147
|
"args": [
|
|
1148
1148
|
{
|
|
1149
1149
|
"index": 0,
|
|
1150
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1150
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1151
1151
|
"requiredTags": ["untrusted"]
|
|
1152
1152
|
}
|
|
1153
1153
|
]
|
|
@@ -1161,7 +1161,7 @@
|
|
|
1161
1161
|
"args": [
|
|
1162
1162
|
{
|
|
1163
1163
|
"index": 0,
|
|
1164
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1164
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1165
1165
|
"requiredTags": ["untrusted"]
|
|
1166
1166
|
}
|
|
1167
1167
|
]
|
|
@@ -1176,7 +1176,7 @@
|
|
|
1176
1176
|
"args": [
|
|
1177
1177
|
{
|
|
1178
1178
|
"index": 0,
|
|
1179
|
-
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen"],
|
|
1179
|
+
"disallowedTags": ["url-encoded", "limited-chars", "alphanum-space-hyphen", "safe-path"],
|
|
1180
1180
|
"requiredTags": ["untrusted"]
|
|
1181
1181
|
}
|
|
1182
1182
|
]
|
|
@@ -55,7 +55,7 @@ module.exports.handle = function handle() {
|
|
|
55
55
|
logger.trace('hooking fastify-static/index');
|
|
56
56
|
tagRangeUtil.addInPlace(
|
|
57
57
|
trackingData.tagRanges,
|
|
58
|
-
new TagRange(0, data.args[0].length - 1, '
|
|
58
|
+
new TagRange(0, data.args[0].length - 1, 'safe-path')
|
|
59
59
|
);
|
|
60
60
|
tagRangeUtil.removeInPlace(trackingData.tagRanges, ['untrusted']);
|
|
61
61
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Copyright: 2023 Contrast Security, Inc
|
|
3
|
+
Contact: support@contrastsecurity.com
|
|
4
|
+
License: Commercial
|
|
5
|
+
|
|
6
|
+
NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
made available through public repositories, use of this Software is subject to
|
|
9
|
+
the applicable End User Licensing Agreement found at
|
|
10
|
+
https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const parseUrl = require('parseurl');
|
|
18
|
+
const requireHook = require('../../hooks/require');
|
|
19
|
+
const patcher = require('../../hooks/patcher');
|
|
20
|
+
const tracker = require('../../tracker');
|
|
21
|
+
const { PATCH_TYPES } = require('../../constants');
|
|
22
|
+
const TagRange = require('../models/tag-range');
|
|
23
|
+
const tagRangeUtil = require('../models/tag-range/util');
|
|
24
|
+
const { CallContext, Signature, PropagationEvent } = require('../models');
|
|
25
|
+
const { AsyncStorage, KEYS } = require('../../core/async-storage');
|
|
26
|
+
const agent = require('../../agent');
|
|
27
|
+
|
|
28
|
+
module.exports.handle = handle;
|
|
29
|
+
module.exports.patchServerStatic = patchServeStatic;
|
|
30
|
+
module.exports.patchSendModule = patchSendModule;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Called to install require hook.
|
|
34
|
+
*/
|
|
35
|
+
function handle() {
|
|
36
|
+
if (agent.config.assess.enable_sanitizer_serve_static) {
|
|
37
|
+
requireHook.resolve(
|
|
38
|
+
{ name: 'serve-static' },
|
|
39
|
+
(serveStatic) =>
|
|
40
|
+
patchServeStatic(serveStatic));
|
|
41
|
+
requireHook.resolve(
|
|
42
|
+
{ name: 'send' },
|
|
43
|
+
(sendModule) =>
|
|
44
|
+
patchSendModule(sendModule));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Patch the function to apply html-encoded tag to tracked return values.
|
|
50
|
+
* @param {object} serveStaticExport The exported value of the serve-static module
|
|
51
|
+
*/
|
|
52
|
+
function patchServeStatic(serveStaticExport) {
|
|
53
|
+
return patcher.patch(serveStaticExport, {
|
|
54
|
+
name: 'serve-static',
|
|
55
|
+
patchType: PATCH_TYPES.ASSESS_PROPAGATOR,
|
|
56
|
+
alwaysRun: true,
|
|
57
|
+
post(data) {
|
|
58
|
+
const resultFn = data.result;
|
|
59
|
+
data.result = patcher.patch(resultFn, {
|
|
60
|
+
name: 'serve-static',
|
|
61
|
+
patchType: PATCH_TYPES.ASSESS_PROPAGATOR,
|
|
62
|
+
alwaysRun: true,
|
|
63
|
+
pre(data) {
|
|
64
|
+
const req = data.args[0];
|
|
65
|
+
const path = parseUrl(req).pathname;
|
|
66
|
+
|
|
67
|
+
AsyncStorage.set(KEYS.SERVE_STATIC_PATH, path);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function patchSendModule(sendModuleExport) {
|
|
75
|
+
return patcher.patch(sendModuleExport, {
|
|
76
|
+
name: 'send',
|
|
77
|
+
patchType: PATCH_TYPES.ASSESS_PROPAGATOR,
|
|
78
|
+
alwaysRun: true,
|
|
79
|
+
pre(data) {
|
|
80
|
+
const [req, path] = data.args;
|
|
81
|
+
const serveStaticPath = AsyncStorage.get(KEYS.SERVE_STATIC_PATH);
|
|
82
|
+
const reqPath = parseUrl(req).pathname;
|
|
83
|
+
|
|
84
|
+
if (path !== serveStaticPath || path !== reqPath) return;
|
|
85
|
+
|
|
86
|
+
const trackData = tracker.getData(path);
|
|
87
|
+
if (trackData) {
|
|
88
|
+
trackData.tagRanges = tagRangeUtil.add(
|
|
89
|
+
trackData.tagRanges,
|
|
90
|
+
new TagRange(0, path.length - 1, 'safe-path')
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const event = new PropagationEvent({
|
|
94
|
+
context: CallContext.create(data),
|
|
95
|
+
parents: [trackData.event],
|
|
96
|
+
signature: new Signature(
|
|
97
|
+
'serve-static'
|
|
98
|
+
),
|
|
99
|
+
source: 'P',
|
|
100
|
+
target: 'R',
|
|
101
|
+
tags: 'safe-path',
|
|
102
|
+
tagRanges: trackData.tagRanges
|
|
103
|
+
});
|
|
104
|
+
trackData.event = event;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
package/lib/constants.js
CHANGED
|
@@ -515,6 +515,7 @@ const TAGS = {
|
|
|
515
515
|
'CUSTOM_VALIDATED_UNVALIDATED_REDIRECT',
|
|
516
516
|
'custom-validated-xpath-injection': 'CUSTOM_VALIDATED_XPATH_INJECTION',
|
|
517
517
|
'custom-validated-xxe': 'CUSTOM_VALIDATED_XXE',
|
|
518
|
+
'safe-path': 'SAFE_PATH',
|
|
518
519
|
|
|
519
520
|
// tracked-string range tags
|
|
520
521
|
'no-newlines': 'NO_NEWLINES',
|
package/lib/contrast.js
CHANGED
|
@@ -330,6 +330,7 @@ contrastAgent.bootstrap = function(args) {
|
|
|
330
330
|
const args = {
|
|
331
331
|
quiet: (process.env['CONTRAST__AGENT__DIAGNOSTICS__QUIET'] === 'false') ? false : true,
|
|
332
332
|
output: destination,
|
|
333
|
+
skip: false,
|
|
333
334
|
};
|
|
334
335
|
|
|
335
336
|
try {
|
|
@@ -339,8 +340,9 @@ contrastAgent.bootstrap = function(args) {
|
|
|
339
340
|
}
|
|
340
341
|
|
|
341
342
|
if (!(process.env['CONTRAST__AGENT__SYSTEM_DIAGNOSTICS__ENABLE'] === 'false')) {
|
|
343
|
+
args.output = path.join(args.output, '../contrast_system_info.json');
|
|
342
344
|
const info = fetchSystemInfo();
|
|
343
|
-
outputSystemInfo(
|
|
345
|
+
outputSystemInfo(args, info);
|
|
344
346
|
}
|
|
345
347
|
}
|
|
346
348
|
});
|
|
@@ -38,6 +38,7 @@ const KEYS = {
|
|
|
38
38
|
FASTIFY_HANDLER_RESOLVED: 'fastify.xss.handler.resolved',
|
|
39
39
|
FASTIFY_REPLY_SEND_STATE: 'fastify.xss.reply.send.state',
|
|
40
40
|
FINALHANDLER_CB_INDEX: 'finalHandlerCbIndex',
|
|
41
|
+
SERVE_STATIC_PATH: 'serveStaticPath',
|
|
41
42
|
HAPI_CALLER: 'hapi.caller',
|
|
42
43
|
INPUT_EXCLUSIONS: 'defend.exclusions',
|
|
43
44
|
KOA_CTX: 'koa.ctx',
|
|
@@ -721,6 +721,14 @@ const assess = [
|
|
|
721
721
|
default: false,
|
|
722
722
|
desc: 'trust incoming strings when they pass custom validators (Mongoose, Joi)',
|
|
723
723
|
},
|
|
724
|
+
{
|
|
725
|
+
name: 'assess.enable_sanitizer_serve_static',
|
|
726
|
+
arg: '<enable-sanitizer-serve-static>',
|
|
727
|
+
default: false,
|
|
728
|
+
desc: 'Enables the serve-static module as a path-traversal sanitizer. Express uses serve-static in a safe way' +
|
|
729
|
+
'but manual setup of serve-static can be vulnerable. Even with Express there is a possibility for "traversing-down"' +
|
|
730
|
+
'the served folder or user misconfiguration if not configured with an absolute path',
|
|
731
|
+
},
|
|
724
732
|
{
|
|
725
733
|
name: 'assess.enable_preflight',
|
|
726
734
|
arg: '[false]',
|
|
@@ -114,7 +114,10 @@ function getProtectValues(option, config, tsData) {
|
|
|
114
114
|
Source: 'ContrastUI',
|
|
115
115
|
};
|
|
116
116
|
} else if (option.name.includes('protect.rules') && option.name.includes('mode')) {
|
|
117
|
-
|
|
117
|
+
let apiRule;
|
|
118
|
+
if (tsData.protectionRulesList) {
|
|
119
|
+
apiRule = tsData.protectionRulesList.find(r => r.id == option.name.split('.')[2]);
|
|
120
|
+
}
|
|
118
121
|
|
|
119
122
|
const apiMode = apiRule ? apiRule.mode : undefined;
|
|
120
123
|
const ymlMode = config._flat[option.name];
|
|
@@ -199,6 +202,14 @@ function outputAgentConfigFile(agent, options, args, err) {
|
|
|
199
202
|
effectiveConfig.Config.Status = 'Success';
|
|
200
203
|
}
|
|
201
204
|
|
|
205
|
+
try {
|
|
206
|
+
fs.accessSync(path.join(args.output, '..'), fs.constants.RDWD_OK);
|
|
207
|
+
fs.writeFileSync(args.output, JSON.stringify(effectiveConfig, null, 2), 'utf-8');
|
|
208
|
+
} catch (err) {
|
|
209
|
+
// try to write the file at pwd instead
|
|
210
|
+
args.output = path.join(process.cwd(), 'contrast_effective_config.json');
|
|
211
|
+
}
|
|
212
|
+
|
|
202
213
|
try {
|
|
203
214
|
fs.accessSync(path.join(args.output, '..'), fs.constants.RDWD_OK);
|
|
204
215
|
fs.writeFileSync(args.output, JSON.stringify(effectiveConfig, null, 2), 'utf-8');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agent",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.31.0",
|
|
4
4
|
"description": "Node.js security instrumentation by Contrast Security",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"security",
|
|
@@ -182,6 +182,7 @@
|
|
|
182
182
|
"qs": "^6.9.4",
|
|
183
183
|
"rethinkdb": "file:test/mock/rethinkdb",
|
|
184
184
|
"sequelize": "^6.11.0",
|
|
185
|
+
"serve-static": "^1.15.0",
|
|
185
186
|
"shellcheck": "^1.0.0",
|
|
186
187
|
"sinon": "^9.2.4",
|
|
187
188
|
"sinon-chai": "^3.3.0",
|