@@ -59,5 +59,116 @@ export class ScrubbingTest {
59
59
const scrubbedValue = new TrustedValue ( scrubber . scrubValue ( "[email protected] " ) ) ;
60
60
expect ( scrubber . scrub ( { key : scrubbedValue } ) ) . to . deep . equal ( { key : "[redacted:email]" } ) ;
61
61
}
62
+
63
+ @test public testAnalyticsProperties_URLScrubbing ( ) {
64
+ // Test case that mirrors the analytics.track() usage pattern
65
+ const mockInstance = {
66
+ id : "test-instance-123" ,
67
+ workspaceId : "test-workspace-456" ,
68
+ stoppingTime : "2023-01-01T00:00:00.000Z" ,
69
+ status : {
70
+ conditions : [
71
+ {
72
+ message :
73
+ "Content initialization failed: cannot initialize workspace: git initializer gitClone: git clone --depth=1 --shallow-submodules https://gitlab.com/acme-corp/web/frontend/services/deployment-manager.git --config http.version=HTTP/1.1 . failed (exit status 128):" ,
74
+ } ,
75
+ {
76
+ message : "Another error with URL: https://github.com/user/repo.git" ,
77
+ } ,
78
+ {
79
+ message : "Error without URL" ,
80
+ } ,
81
+ {
82
+ message : "API call to https://api.example.com/endpoint failed" ,
83
+ } ,
84
+ ] ,
85
+ timeout : false ,
86
+ } ,
87
+ } ;
88
+
89
+ // This mirrors the exact usage in workspace-instance-controller.ts
90
+ const scrubbedProperties = scrubber . scrub ( {
91
+ instanceId : mockInstance . id ,
92
+ workspaceId : mockInstance . workspaceId ,
93
+ stoppingTime : new Date ( mockInstance . stoppingTime ) ,
94
+ conditions : mockInstance . status . conditions ,
95
+ timeout : mockInstance . status . timeout ,
96
+ } ) ;
97
+
98
+ // Verify workspaceId is hashed (field-based scrubbing)
99
+ expect ( scrubbedProperties . workspaceId ) . to . match ( / ^ \[ r e d a c t e d : m d 5 : [ a - f 0 - 9 ] { 32 } \] $ / ) ;
100
+
101
+ // Verify instanceId is not scrubbed (not in sensitive fields)
102
+ expect ( scrubbedProperties . instanceId ) . to . equal ( "test-instance-123" ) ;
103
+
104
+ // Verify URLs in nested conditions are hashed (pattern-based scrubbing)
105
+ expect ( scrubbedProperties . conditions [ 0 ] . message ) . to . include ( "[redacted:md5:" ) ;
106
+ expect ( scrubbedProperties . conditions [ 0 ] . message ) . to . include ( ":url]" ) ;
107
+ expect ( scrubbedProperties . conditions [ 0 ] . message ) . to . not . include ( "gitlab.com" ) ;
108
+
109
+ expect ( scrubbedProperties . conditions [ 1 ] . message ) . to . include ( "[redacted:md5:" ) ;
110
+ expect ( scrubbedProperties . conditions [ 1 ] . message ) . to . include ( ":url]" ) ;
111
+ expect ( scrubbedProperties . conditions [ 1 ] . message ) . to . not . include ( "github.com" ) ;
112
+
113
+ // Verify non-URL message is unchanged
114
+ expect ( scrubbedProperties . conditions [ 2 ] . message ) . to . equal ( "Error without URL" ) ;
115
+
116
+ // Verify non-.git URL is NOT scrubbed
117
+ expect ( scrubbedProperties . conditions [ 3 ] . message ) . to . equal (
118
+ "API call to https://api.example.com/endpoint failed" ,
119
+ ) ;
120
+ expect ( scrubbedProperties . conditions [ 3 ] . message ) . to . not . include ( "[redacted:md5:" ) ;
121
+
122
+ // Verify other properties are preserved
123
+ expect ( scrubbedProperties . timeout ) . to . equal ( false ) ;
124
+ // Date objects get converted to empty objects by the scrubber since they don't have enumerable properties
125
+ expect ( scrubbedProperties . stoppingTime ) . to . be . an ( "object" ) ;
126
+ }
127
+
128
+ @test public testURL_PatternScrubbing ( ) {
129
+ // Test individual URL scrubbing for .git URLs
130
+ const urlMessage = "git clone https://gitlab.com/acme-corp/web/frontend/services/deployment-manager.git failed" ;
131
+ const scrubbedMessage = scrubber . scrubValue ( urlMessage ) ;
132
+
133
+ expect ( scrubbedMessage ) . to . include ( "[redacted:md5:" ) ;
134
+ expect ( scrubbedMessage ) . to . include ( ":url]" ) ;
135
+ expect ( scrubbedMessage ) . to . not . include ( "gitlab.com" ) ;
136
+ expect ( scrubbedMessage ) . to . include ( "git clone" ) ;
137
+ expect ( scrubbedMessage ) . to . include ( "failed" ) ;
138
+ }
139
+
140
+ @test public testURL_NonGitURLsNotScrubbed ( ) {
141
+ // Test that non-.git URLs are NOT scrubbed
142
+ const apiMessage = "API call to https://api.example.com/endpoint failed" ;
143
+ const scrubbedMessage = scrubber . scrubValue ( apiMessage ) ;
144
+
145
+ // Non-.git URLs should remain unchanged
146
+ expect ( scrubbedMessage ) . to . equal ( "API call to https://api.example.com/endpoint failed" ) ;
147
+ expect ( scrubbedMessage ) . to . not . include ( "[redacted:md5:" ) ;
148
+ }
149
+
150
+ @test public testURL_MixedURLTypes ( ) {
151
+ // Test message with both .git and non-.git URLs
152
+ const mixedMessage = "Clone from https://github.com/user/repo.git then visit https://docs.gitpod.io/configure" ;
153
+ const scrubbedMessage = scrubber . scrubValue ( mixedMessage ) ;
154
+
155
+ // .git URL should be scrubbed
156
+ expect ( scrubbedMessage ) . to . include ( "[redacted:md5:" ) ;
157
+ expect ( scrubbedMessage ) . to . include ( ":url]" ) ;
158
+ expect ( scrubbedMessage ) . to . not . include ( "github.com/user/repo.git" ) ;
159
+
160
+ // Non-.git URL should remain unchanged
161
+ expect ( scrubbedMessage ) . to . include ( "https://docs.gitpod.io/configure" ) ;
162
+ }
163
+
164
+ @test public testURL_HttpGitURLs ( ) {
165
+ // Test that http:// .git URLs are also scrubbed
166
+ const httpMessage = "git clone http://internal-git.company.com/project.git" ;
167
+ const scrubbedMessage = scrubber . scrubValue ( httpMessage ) ;
168
+
169
+ expect ( scrubbedMessage ) . to . include ( "[redacted:md5:" ) ;
170
+ expect ( scrubbedMessage ) . to . include ( ":url]" ) ;
171
+ expect ( scrubbedMessage ) . to . not . include ( "internal-git.company.com" ) ;
172
+ }
62
173
}
63
174
module . exports = new ScrubbingTest ( ) ;
0 commit comments