Skip to content

Commit b844100

Browse files
committed
Apply credential redaction to container logs
Previous fix only covered SDK client. Container-side logging in git-service.ts still exposed credentials in error messages and logs. Created shared utility in @repo/shared for consistent redaction.
1 parent 823ce53 commit b844100

File tree

5 files changed

+55
-16
lines changed

5 files changed

+55
-16
lines changed

packages/sandbox-container/src/services/git-service.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Git Operations Service
22

33
import type { Logger } from '@repo/shared';
4+
import { redactCredentials } from '@repo/shared';
45
import type {
56
GitErrorContext,
67
ValidationFailedContext
@@ -52,7 +53,7 @@ export class GitService {
5253
return {
5354
success: false,
5455
error: {
55-
message: `Invalid Git URL '${repoUrl}': ${urlValidation.errors.join(
56+
message: `Invalid Git URL '${redactCredentials(repoUrl)}': ${urlValidation.errors.join(
5657
', '
5758
)}`,
5859
code: ErrorCode.INVALID_GIT_URL,
@@ -115,7 +116,7 @@ export class GitService {
115116

116117
if (result.exitCode !== 0) {
117118
this.logger.error('Git clone failed', undefined, {
118-
repoUrl,
119+
repoUrl: redactCredentials(repoUrl),
119120
targetDirectory,
120121
exitCode: result.exitCode,
121122
stderr: result.stderr
@@ -129,12 +130,12 @@ export class GitService {
129130
return {
130131
success: false,
131132
error: {
132-
message: `Failed to clone repository '${repoUrl}': ${
133+
message: `Failed to clone repository '${redactCredentials(repoUrl)}': ${
133134
result.stderr || `exit code ${result.exitCode}`
134135
}`,
135136
code: errorCode,
136137
details: {
137-
repository: repoUrl,
138+
repository: redactCredentials(repoUrl),
138139
targetDir: targetDirectory,
139140
exitCode: result.exitCode,
140141
stderr: result.stderr
@@ -190,16 +191,16 @@ export class GitService {
190191
this.logger.error(
191192
'Failed to clone repository',
192193
error instanceof Error ? error : undefined,
193-
{ repoUrl, options }
194+
{ repoUrl: redactCredentials(repoUrl), options }
194195
);
195196

196197
return {
197198
success: false,
198199
error: {
199-
message: `Failed to clone repository '${repoUrl}': ${errorMessage}`,
200+
message: `Failed to clone repository '${redactCredentials(repoUrl)}': ${errorMessage}`,
200201
code: ErrorCode.GIT_CLONE_FAILED,
201202
details: {
202-
repository: repoUrl,
203+
repository: redactCredentials(repoUrl),
203204
targetDir: options.targetDir,
204205
stderr: errorMessage
205206
} satisfies GitErrorContext

packages/sandbox/src/clients/git-client.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { GitCheckoutResult } from '@repo/shared';
2+
import { redactCredentials } from '@repo/shared';
23
import { BaseHttpClient } from './base-client';
34
import type { HttpClientOptions, SessionRequest } from './types';
45

@@ -58,7 +59,7 @@ export class GitClient extends BaseHttpClient {
5859
data
5960
);
6061

61-
const redactedUrl = this.redactCredentials(repoUrl);
62+
const redactedUrl = redactCredentials(repoUrl);
6263
this.logSuccess(
6364
'Repository cloned',
6465
`${redactedUrl} (branch: ${response.branch}) -> ${response.targetDir}`
@@ -89,12 +90,4 @@ export class GitClient extends BaseHttpClient {
8990
return repoName.replace(/\.git$/, '') || 'repo';
9091
}
9192
}
92-
93-
/**
94-
* Redact credentials from repository URLs for secure logging
95-
*/
96-
private redactCredentials(repoUrl: string): string {
97-
// Replace any credentials (username:password or token) between :// and @ with ******
98-
return repoUrl.replace(/\/\/[^@]+@/, '//******@');
99-
}
10093
}

packages/shared/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,5 @@ export type {
9898
WriteFileResult
9999
} from './types.js';
100100
export { isExecResult, isProcess, isProcessStatus } from './types.js';
101+
// Export URL functions
102+
export { redactCredentials } from './url.js';

packages/shared/src/url.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Redact credentials from URLs for secure logging
3+
*
4+
* Replaces any credentials (username:password, tokens, etc.) embedded
5+
* in URLs with ****** to prevent sensitive data exposure in logs.
6+
*
7+
* @param url - The URL that may contain credentials
8+
* @returns URL with credentials redacted
9+
*
10+
* @example
11+
* redactCredentials('https://token:[email protected]/repo.git')
12+
* // Returns: 'https://******@github.com/repo.git'
13+
*
14+
* redactCredentials('https://github.com/repo.git')
15+
* // Returns: 'https://github.com/repo.git' (unchanged)
16+
*/
17+
export function redactCredentials(url: string): string {
18+
// Replace any credentials between :// and @ with ******
19+
// Pattern matches: ://[anything]@ -> ://******@
20+
return url.replace(/:\/\/([^@/]+)@/, '://******@');
21+
}

packages/shared/tests/url.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { redactCredentials } from '../src/url';
3+
4+
describe('redactCredentials', () => {
5+
it('should redact credentials and preserve public URLs', () => {
6+
// Credentials with username:password format
7+
expect(redactCredentials('https://user:[email protected]/repo.git'))
8+
.toBe('https://******@github.com/repo.git');
9+
10+
// Token without username (different format)
11+
expect(redactCredentials('https://[email protected]/org/project.git'))
12+
.toBe('https://******@github.com/org/project.git');
13+
14+
// Public URLs without credentials
15+
expect(redactCredentials('https://github.com/facebook/react.git'))
16+
.toBe('https://github.com/facebook/react.git');
17+
18+
// SSH URLs (different format, @ not after protocol)
19+
expect(redactCredentials('[email protected]:user/repo.git'))
20+
.toBe('[email protected]:user/repo.git');
21+
});
22+
});

0 commit comments

Comments
 (0)