Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/playwright-core/src/tools/backend/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,15 @@ export class Context {
code: `process.env['${secretName}']`,
};
}

redactSecrets(text: string): string {
for (const [secretName, secretValue] of Object.entries(this.config.secrets ?? {})) {
if (!secretValue)
continue;
text = text.replaceAll(secretValue, `<secret>${secretName}</secret>`);
}
return text;
}
}

function originOrHostGlob(originOrHost: string) {
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/tools/backend/logFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export class LogFile {
return;
this._file ??= await this._context.outputFile({ prefix: this._filePrefix, ext: 'log', date: new Date(this._startTime) }, { origin: 'code' });
const relativeTime = Math.round(wallTime - this._startTime);
const logLine = `[${String(relativeTime).padStart(8, ' ')}ms] ${text}\n`;
const logLine = `[${String(relativeTime).padStart(8, ' ')}ms] ${this._context.redactSecrets(text)}\n`;
await fs.promises.appendFile(this._file, logLine);

const lineCount = logLine.split('\n').length - 1;
Expand Down
14 changes: 2 additions & 12 deletions packages/playwright-core/src/tools/backend/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export class Response {

private async _writeFile(resolvedFile: ResolvedFile, data: Buffer | string | null) {
if (typeof data === 'string')
await fs.promises.writeFile(resolvedFile.fileName, this._redactSecrets(data), 'utf-8');
await fs.promises.writeFile(resolvedFile.fileName, this._context.redactSecrets(data), 'utf-8');
else if (data)
await fs.promises.writeFile(resolvedFile.fileName, data);
this._writtenFiles.add(path.resolve(resolvedFile.fileName));
Expand Down Expand Up @@ -155,16 +155,6 @@ export class Response {
this._includeSnapshotRoot = root;
}

private _redactSecrets(text: string): string {
for (const [secretName, secretValue] of Object.entries(this._context.config.secrets ?? {})) {
if (!secretValue)
continue;
text = text.replaceAll(secretValue, `<secret>${secretName}</secret>`);
}
return text;
}


async serialize(): Promise<CallToolResult> {
const allSections = await this._build();
await this._enforceOutputBudget();
Expand Down Expand Up @@ -211,7 +201,7 @@ export class Response {
const content: (TextContent | ImageContent)[] = [
{
type: 'text',
text: sanitizeUnicode(this._redactSecrets(serializedText)),
text: sanitizeUnicode(this._context.redactSecrets(serializedText)),
}
];

Expand Down
39 changes: 38 additions & 1 deletion tests/mcp/secrets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import fs from 'node:fs';

import { test, expect } from './fixtures';
import { test, expect, parseResponse, consoleEntries } from './fixtures';

test('browser_type', async ({ startClient, server }) => {
const secretsFile = test.info().outputPath('secrets.env');
Expand Down Expand Up @@ -65,6 +65,43 @@ await page.getByRole('textbox').press('Enter');`,
});


test('secrets are redacted in console log artifact', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/41509' } }, async ({ startClient, server }) => {
const secretsFile = test.info().outputPath('secrets.env');
await fs.promises.writeFile(secretsFile, 'X-PASSWORD=password123');

const { client } = await startClient({
args: ['--secrets', secretsFile],
});

server.setContent('/', `
<!DOCTYPE html>
<html>
<input type='keypress' onkeypress="console.log('Key pressed:', event.key, ', Text:', event.target.value)"></input>
</html>
`, 'text/html');

await client.callTool({
name: 'browser_navigate',
arguments: {
url: server.PREFIX,
},
});

const response = parseResponse(await client.callTool({
name: 'browser_type',
arguments: {
element: 'textbox',
target: 'e2',
text: 'X-PASSWORD',
submit: true,
},
}));

const consoleLog = await consoleEntries(response);
expect(consoleLog).not.toContain('password123');
expect(consoleLog).toContain('<secret>X-PASSWORD</secret>');
});

test('browser_fill_form', async ({ startClient, server }) => {
const secretsFile = test.info().outputPath('secrets.env');
await fs.promises.writeFile(secretsFile, 'X-PASSWORD=password123');
Expand Down
Loading