@@ -59,5 +59,116 @@ export class ScrubbingTest {
5959 const scrubbedValue = new TrustedValue ( scrubber . scrubValue ( "foo@bar.com" ) ) ;
6060 expect ( scrubber . scrub ( { key : scrubbedValue } ) ) . to . deep . equal ( { key : "[redacted:email]" } ) ;
6161 }
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+ }
62173}
63174module . exports = new ScrubbingTest ( ) ;
0 commit comments