|
5 | 5 | TelemetrySubscriber, |
6 | 6 | createTelemetryReporter, |
7 | 7 | createBeforeLogAdapter, |
| 8 | + REPORTER_PERFORMANCE_BUDGETS, |
8 | 9 | TELEMETRY_AGGREGATE_KEYS, |
9 | 10 | LifecycleEmitter, |
10 | 11 | ReporterManager, |
@@ -51,6 +52,25 @@ function rawInput(type: string, payload: unknown): IReporterEmitEventInput<unkno |
51 | 52 | }; |
52 | 53 | } |
53 | 54 |
|
| 55 | +function foreignDiagnosticEnvelope( |
| 56 | + sequence: number, |
| 57 | + privacy: IReporterEventEnvelope<unknown>['privacy'], |
| 58 | + payload: unknown |
| 59 | +): IReporterEventEnvelope<unknown> { |
| 60 | + return { |
| 61 | + protocolVersion: { major: 1, minor: 0 }, |
| 62 | + eventId: `foreign_${sequence}`, |
| 63 | + sessionId: 'foreign-session', |
| 64 | + sequence, |
| 65 | + timestamp: '2026-08-28T00:00:00.000Z', |
| 66 | + source: { packageName: '@foreign/reporter-plugin', packageVersion: '1.0.0' }, |
| 67 | + privacy, |
| 68 | + required: true, |
| 69 | + type: 'diagnosticEmitted', |
| 70 | + payload |
| 71 | + }; |
| 72 | +} |
| 73 | + |
54 | 74 | describe('TelemetrySubscriber', () => { |
55 | 75 | it('produces an allowlisted aggregate from the event stream before reporter filtering', async () => { |
56 | 76 | const telemetry: TelemetrySubscriber = new TelemetrySubscriber(); |
@@ -152,6 +172,174 @@ describe('TelemetrySubscriber', () => { |
152 | 172 | } |
153 | 173 | }); |
154 | 174 |
|
| 175 | + it('rejects hostile non-public diagnostic fields from foreign envelopes', async () => { |
| 176 | + const TOKEN_CODE: string = 'ghp_super_secret_token'; |
| 177 | + const TOKEN_CATEGORY: string = 'token=super-secret-value'; |
| 178 | + const PATH_CODE: string = '/home/user/private/.npmrc'; |
| 179 | + const PATH_CATEGORY: string = 'C:\\Users\\private\\rush.json'; |
| 180 | + const telemetry: TelemetrySubscriber = new TelemetrySubscriber(); |
| 181 | + const manager: ReporterManager = new ReporterManager(); |
| 182 | + manager.addReporter(createTelemetryReporter(telemetry)); |
| 183 | + await manager.initializeAsync(); |
| 184 | + |
| 185 | + manager.ingestForeignEnvelope( |
| 186 | + foreignDiagnosticEnvelope(1, 'local-sensitive', { |
| 187 | + code: PATH_CODE, |
| 188 | + category: TOKEN_CATEGORY |
| 189 | + }) |
| 190 | + ); |
| 191 | + manager.ingestForeignEnvelope( |
| 192 | + foreignDiagnosticEnvelope(2, 'secret', { |
| 193 | + code: TOKEN_CODE, |
| 194 | + category: PATH_CATEGORY |
| 195 | + }) |
| 196 | + ); |
| 197 | + manager.ingestForeignEnvelope( |
| 198 | + foreignDiagnosticEnvelope(3, 'local-sensitive', { |
| 199 | + code: 'RUSH_OPERATION_FAILED', |
| 200 | + category: 'operation' |
| 201 | + }) |
| 202 | + ); |
| 203 | + manager.ingestForeignEnvelope( |
| 204 | + foreignDiagnosticEnvelope(4, 'secret', { |
| 205 | + code: 'RUSH_DEPENDENCY_TOOL_FAILED', |
| 206 | + category: 'dependency-tool' |
| 207 | + }) |
| 208 | + ); |
| 209 | + await manager.flushAsync(); |
| 210 | + |
| 211 | + const aggregate: ITelemetryAggregate = telemetry.buildAggregate(); |
| 212 | + expect(aggregate.diagnosticCodes).toEqual(['RUSH_DEPENDENCY_TOOL_FAILED', 'RUSH_OPERATION_FAILED']); |
| 213 | + expect(aggregate.diagnosticCategoryCounts).toEqual({ |
| 214 | + other: 2, |
| 215 | + operation: 1, |
| 216 | + 'dependency-tool': 1 |
| 217 | + }); |
| 218 | + const serialized: string = JSON.stringify(aggregate); |
| 219 | + for (const forbidden of [TOKEN_CODE, TOKEN_CATEGORY, PATH_CODE, PATH_CATEGORY]) { |
| 220 | + expect(serialized).not.toContain(forbidden); |
| 221 | + } |
| 222 | + }); |
| 223 | + |
| 224 | + it('preserves allowlisted diagnostics across mixed privacy ordering', async () => { |
| 225 | + const telemetry: TelemetrySubscriber = new TelemetrySubscriber(); |
| 226 | + const manager: ReporterManager = new ReporterManager(); |
| 227 | + manager.addReporter(createTelemetryReporter(telemetry)); |
| 228 | + await manager.initializeAsync(); |
| 229 | + |
| 230 | + manager.ingestForeignEnvelope( |
| 231 | + foreignDiagnosticEnvelope(1, 'secret', { |
| 232 | + code: 'RUSH_DEPENDENCY_TOOL_FAILED', |
| 233 | + category: 'dependency-tool' |
| 234 | + }) |
| 235 | + ); |
| 236 | + manager.ingestForeignEnvelope( |
| 237 | + foreignDiagnosticEnvelope(2, 'public', { |
| 238 | + code: 'RUSH_OPERATION_FAILED', |
| 239 | + category: 'operation' |
| 240 | + }) |
| 241 | + ); |
| 242 | + manager.ingestForeignEnvelope( |
| 243 | + foreignDiagnosticEnvelope(3, 'local-sensitive', { |
| 244 | + code: 'RUSH_CONFIG_INVALID_JSON', |
| 245 | + category: 'configuration' |
| 246 | + }) |
| 247 | + ); |
| 248 | + manager.ingestForeignEnvelope( |
| 249 | + foreignDiagnosticEnvelope(4, 'secret', { |
| 250 | + code: 'RUSH_NOT_REGISTERED_PRIVATE', |
| 251 | + category: 'future-private-category' |
| 252 | + }) |
| 253 | + ); |
| 254 | + manager.ingestForeignEnvelope( |
| 255 | + foreignDiagnosticEnvelope(5, 'public', { |
| 256 | + code: 'RUSH_FUTURE_PUBLIC_CODE', |
| 257 | + category: 'future-public-category' |
| 258 | + }) |
| 259 | + ); |
| 260 | + await manager.flushAsync(); |
| 261 | + |
| 262 | + expect(telemetry.buildAggregate()).toMatchObject({ |
| 263 | + diagnosticCodes: [ |
| 264 | + 'RUSH_CONFIG_INVALID_JSON', |
| 265 | + 'RUSH_DEPENDENCY_TOOL_FAILED', |
| 266 | + 'RUSH_FUTURE_PUBLIC_CODE', |
| 267 | + 'RUSH_OPERATION_FAILED' |
| 268 | + ], |
| 269 | + diagnosticCategoryCounts: { |
| 270 | + configuration: 1, |
| 271 | + 'dependency-tool': 1, |
| 272 | + operation: 1, |
| 273 | + other: 2 |
| 274 | + } |
| 275 | + }); |
| 276 | + }); |
| 277 | + |
| 278 | + it('bounds diagnostic dimensions deterministically under cardinality flooding', async () => { |
| 279 | + const publicCodes: string[] = []; |
| 280 | + for ( |
| 281 | + let index: number = 0; |
| 282 | + index < REPORTER_PERFORMANCE_BUDGETS.maxTelemetryDiagnosticCodes * 3; |
| 283 | + index++ |
| 284 | + ) { |
| 285 | + publicCodes.push(`RUSH_FOREIGN_CODE${String(index).padStart(3, '0')}`); |
| 286 | + } |
| 287 | + const hostilePrivateCodes: string[] = publicCodes.map((code: string): string => `${code}_PRIVATE`); |
| 288 | + const payloads: Array<{ |
| 289 | + privacy: IReporterEventEnvelope<unknown>['privacy']; |
| 290 | + code: string; |
| 291 | + category: string; |
| 292 | + }> = [ |
| 293 | + ...publicCodes.map((code: string, index: number) => ({ |
| 294 | + privacy: 'public' as const, |
| 295 | + code, |
| 296 | + category: `/private/category/${index}` |
| 297 | + })), |
| 298 | + ...hostilePrivateCodes.map((code: string, index: number) => ({ |
| 299 | + privacy: index % 2 === 0 ? ('local-sensitive' as const) : ('secret' as const), |
| 300 | + code, |
| 301 | + category: `token-${index}` |
| 302 | + })), |
| 303 | + { privacy: 'secret', code: 'RUSH_OPERATION_FAILED', category: 'operation' }, |
| 304 | + { |
| 305 | + privacy: 'local-sensitive', |
| 306 | + code: 'RUSH_DEPENDENCY_TOOL_FAILED', |
| 307 | + category: 'dependency-tool' |
| 308 | + } |
| 309 | + ]; |
| 310 | + |
| 311 | + async function aggregatePayloads(orderedPayloads: typeof payloads): Promise<ITelemetryAggregate> { |
| 312 | + const telemetry: TelemetrySubscriber = new TelemetrySubscriber(); |
| 313 | + const manager: ReporterManager = new ReporterManager(); |
| 314 | + manager.addReporter(createTelemetryReporter(telemetry)); |
| 315 | + await manager.initializeAsync(); |
| 316 | + orderedPayloads.forEach((payload, index: number) => { |
| 317 | + manager.ingestForeignEnvelope( |
| 318 | + foreignDiagnosticEnvelope(index + 1, payload.privacy, { |
| 319 | + code: payload.code, |
| 320 | + category: payload.category |
| 321 | + }) |
| 322 | + ); |
| 323 | + }); |
| 324 | + await manager.flushAsync(); |
| 325 | + return telemetry.buildAggregate(); |
| 326 | + } |
| 327 | + |
| 328 | + const forward: ITelemetryAggregate = await aggregatePayloads(payloads); |
| 329 | + const reverse: ITelemetryAggregate = await aggregatePayloads([...payloads].reverse()); |
| 330 | + expect(reverse.diagnosticCodes).toEqual(forward.diagnosticCodes); |
| 331 | + expect(forward.diagnosticCodes).toHaveLength(REPORTER_PERFORMANCE_BUDGETS.maxTelemetryDiagnosticCodes); |
| 332 | + expect(forward.diagnosticCodes).toContain('RUSH_OPERATION_FAILED'); |
| 333 | + expect(forward.diagnosticCodes).toContain('RUSH_DEPENDENCY_TOOL_FAILED'); |
| 334 | + expect(forward.diagnosticCodes).not.toContain(hostilePrivateCodes[0]); |
| 335 | + expect(forward.diagnosticCategoryCounts).toEqual({ |
| 336 | + other: publicCodes.length + hostilePrivateCodes.length, |
| 337 | + operation: 1, |
| 338 | + 'dependency-tool': 1 |
| 339 | + }); |
| 340 | + expect(Object.keys(forward.diagnosticCategoryCounts)).toHaveLength(3); |
| 341 | + }); |
| 342 | + |
155 | 343 | it('projects public envelopes while preserving allowlisted diagnostic fields deterministically', async () => { |
156 | 344 | const PUBLIC_EXTENSION_SOURCE: IReporterEventSource = { |
157 | 345 | packageName: '@rushstack/public-reporter-plugin', |
|
0 commit comments