fix: truncate long urls in concise network request output (#2513)

## What

Implements the existing `// TODO truncate the URL` in
`src/formatters/NetworkFormatter.ts`: URLs in the concise one-line
network request format are now capped at 150 characters, with the file's
existing `getSizeLimitedString` helper appending the same `...
<truncated>` marker already used for request/response bodies.

## Why

`list_network_requests` emits one concise line per request, so a single
page with a few `data:` URLs (which can be tens of kilobytes each) can
blow up the tool output by orders of magnitude. Truncating in the
concise format keeps list output token-friendly while losing nothing
that matters for identifying a request.

Scope is deliberately narrow — only
`convertNetworkRequestConciseToString` changes:

- the detailed view (`## Request <url>` heading) still shows the full
URL, and
- `toJSON()` / structured content still carries the full URL,

so nothing that needs the complete URL is affected. (Concise
redirect-chain lines inside the detailed view use the same function and
are truncated too, which keeps that list compact as well.)

## Limit rationale

150 characters keeps origin + path + the start of the query string
intact for virtually all real-world URLs (typical URLs are well under
100 characters), while capping pathological `data:`/blob payloads. It
reuses the existing `getSizeLimitedString` pattern with a new
`URL_CONTEXT_SIZE_LIMIT` constant next to `BODY_CONTEXT_SIZE_LIMIT`.

## Before / after

Before:

```
reqid=12 GET data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA... (10,000+ more chars) ...5CYII= [200]
```

After:

```
reqid=12 GET data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...(150 chars total)... <truncated> [200]
```

Short URLs are unchanged.

## Testing

- `npm run build` — pass
- `npm run test:no-build` — pass (all tests, exit 0)
- `npm run check-format` — pass (eslint + prettier)

Added three cases to `tests/formatters/NetworkFormatter.test.ts`:

- long URL is truncated at 150 chars with the `... <truncated>` marker
(and `toJSON()` keeps the full URL),
- `data:` URL is truncated,
- a URL exactly at the limit is left untouched.

No existing snapshots changed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: ZayanKhan-12 <khanzayan200@gmail.com>
Co-authored-by: Alex Rudenko <alexrudenko@chromium.org>
This commit is contained in:
Zayan Khan
2026-09-01 15:36:05 +00:00
committed by GitHub
parent 6a67552771
commit 552c870d3c
2 changed files with 50 additions and 2 deletions
+5 -2
View File
@@ -13,6 +13,7 @@ import {
} from '../third_party/index.js';
const BODY_CONTEXT_SIZE_LIMIT = 10000;
const URL_CONTEXT_SIZE_LIMIT = 255;
export interface NetworkFormatterOptions {
requestId?: number | string;
@@ -263,8 +264,10 @@ function getSizeLimitedString(text: string, sizeLimit: number) {
function convertNetworkRequestConciseToString(
data: NetworkRequestConcise,
): string {
// TODO truncate the URL
return `reqid=${data.requestId} ${data.method} ${data.url} [${data.status}]${data.selectedInDevToolsUI ? ` [selected in the DevTools Network panel]` : ''}`;
// Long URLs (e.g., data: URLs) bloat the concise list output. The full URL
// remains available via the detailed view and the structured content.
const url = getSizeLimitedString(data.url, URL_CONTEXT_SIZE_LIMIT);
return `reqid=${data.requestId} ${data.method} ${url} [${data.status}]${data.selectedInDevToolsUI ? ` [selected in the DevTools Network panel]` : ''}`;
}
function formatHeaders(headers: Record<string, string>): string[] {
+45
View File
@@ -132,6 +132,51 @@ describe('NetworkFormatter', () => {
'reqid=1 GET http://example.com [pending] [selected in the DevTools Network panel]',
);
});
it('truncates long urls', async () => {
const longUrl = `http://example.com/${'a'.repeat(500)}`;
const request = getMockRequest({url: longUrl});
const formatter = await NetworkFormatter.from(request, {
requestId: 1,
saveFile: async () => ({filename: ''}),
redactNetworkHeaders: false,
});
assert.equal(
formatter.toString(),
`reqid=1 GET ${longUrl.substring(0, 255)}... <truncated> [pending]`,
);
// The structured data keeps the full URL.
assert.equal(formatter.toJSON().url, longUrl);
});
it('truncates data: urls', async () => {
const dataUrl = `data:image/png;base64,${'A'.repeat(5000)}`;
const request = getMockRequest({url: dataUrl});
const formatter = await NetworkFormatter.from(request, {
requestId: 1,
saveFile: async () => ({filename: ''}),
redactNetworkHeaders: false,
});
assert.equal(
formatter.toString(),
`reqid=1 GET ${dataUrl.substring(0, 255)}... <truncated> [pending]`,
);
});
it('does not truncate urls within the size limit', async () => {
// Exactly at the 150 character limit.
const url = `http://example.com/${'a'.repeat(131)}`;
const request = getMockRequest({url});
const formatter = await NetworkFormatter.from(request, {
requestId: 1,
saveFile: async () => ({filename: ''}),
redactNetworkHeaders: false,
});
assert.equal(formatter.toString(), `reqid=1 GET ${url} [pending]`);
});
});
describe('toStringDetailed', () => {