fix(memory): reject contradictory pagination options in take_heapsnapshot (#3002) (#3131)

Validate integer pagination parameters and reject contradictory
pagination options in heap snapshot tools.

Closes #3002
This commit is contained in:
Yuri (IURII) Trukhin authored and GitHub committed 2026-10-14 14:32:07 +00:00
1 parent c84b687a07
commit 508126c925
3 files changed
+52 -34

No files matched your search

+12 -12
View File
@@ -565,8 +565,8 @@ in the DevTools Elements panel (if any).
- **filterName** (enum: "objectsRetainedByDetachedDomNodes", "objectsRetainedByConsole", "objectsRetainedByEventHandlers", "objectsRetainedByContexts", "sharedNativeContext", "noNativeContext", "attributedToSpecificNativeContext") _(optional)_: An optional filter to apply to the nodes.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **objectId** (number) _(optional)_: The object ID (nodeId) of the specific native context to filter by when filterName is attributedToSpecificNativeContext.
- **pageIdx** (number) _(optional)_: The page index for pagination.
- **pageSize** (number) _(optional)_: The page size for pagination.
- **pageIdx** (integer) _(optional)_: The page index for pagination.
- **pageSize** (integer) _(optional)_: The page size for pagination.
---
@@ -580,8 +580,8 @@ in the DevTools Elements panel (if any).
- **filterName** (enum: "objectsRetainedByDetachedDomNodes", "objectsRetainedByConsole", "objectsRetainedByEventHandlers", "objectsRetainedByContexts", "sharedNativeContext", "noNativeContext", "attributedToSpecificNativeContext") _(optional)_: An optional filter to apply to the aggregates.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **objectId** (number) _(optional)_: The object ID (nodeId) of the specific native context to filter by when filterName is attributedToSpecificNativeContext.
- **pageIdx** (number) _(optional)_: The page index for pagination of aggregates.
- **pageSize** (number) _(optional)_: The page size for pagination of aggregates.
- **pageIdx** (integer) _(optional)_: The page index for pagination of aggregates.
- **pageSize** (integer) _(optional)_: The page size for pagination of aggregates.
---
@@ -605,8 +605,8 @@ in the DevTools Elements panel (if any).
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to read.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **pageIdx** (number) _(optional)_: The page index for pagination.
- **pageSize** (number) _(optional)_: The page size for pagination.
- **pageIdx** (integer) _(optional)_: The page index for pagination.
- **pageSize** (integer) _(optional)_: The page size for pagination.
---
@@ -620,8 +620,8 @@ in the DevTools Elements panel (if any).
- **nodeId** (number) **(required)**: The node ID to get outgoing edges for.
- **excludePrimitives** (boolean) _(optional)_: Whether to exclude primitive target nodes. Default is true.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **pageIdx** (number) _(optional)_: The page index for pagination.
- **pageSize** (number) _(optional)_: The page size for pagination.
- **pageIdx** (integer) _(optional)_: The page index for pagination.
- **pageSize** (integer) _(optional)_: The page size for pagination.
- **retainedSize** (string) _(optional)_: Inclusive retained size range (e.g. "1MB-2MB", "-1MB", or "1MB-") for target nodes. A single value is treated as a minimum. Currently, only the lower bound is applied.
- **sortBy** (enum: "retainedSize", "selfSize", "name") _(optional)_: Sort order for edges. Default is retainedSize.
@@ -648,8 +648,8 @@ in the DevTools Elements panel (if any).
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to read.
- **nodeId** (number) **(required)**: The node ID to get retainers for.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **pageIdx** (number) _(optional)_: The page index for pagination.
- **pageSize** (number) _(optional)_: The page size for pagination.
- **pageIdx** (integer) _(optional)_: The page index for pagination.
- **pageSize** (integer) _(optional)_: The page size for pagination.
---
@@ -690,8 +690,8 @@ in the DevTools Elements panel (if any).
- **isDetached** (boolean) _(optional)_: Whether to filter for detached DOM nodes.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **nodeType** (string) _(optional)_: Optional V8 node type filter (e.g. object, closure, string, array, code).
- **pageIdx** (number) _(optional)_: The page index for pagination.
- **pageSize** (number) _(optional)_: The page size for pagination.
- **pageIdx** (integer) _(optional)_: The page index for pagination.
- **pageSize** (integer) _(optional)_: The page size for pagination.
- **propertyName** (string) _(optional)_: Optional property name filter for outgoing reference edges.
- **retainedSize** (string) _(optional)_: Inclusive retained size range (e.g. "1MB-2MB", "-1MB", or "1MB-"). A single value is treated as a minimum.
- **selfSize** (string) _(optional)_: Inclusive self size range (e.g. "1MB-2MB", "-1MB", or "1MB-"). A single value is treated as a minimum.
+12 -12
View File
@@ -584,13 +584,13 @@ export const commands: Commands = {
},
pageIdx: {
name: 'pageIdx',
type: 'number',
type: 'integer',
description: 'The page index for pagination.',
required: false,
},
pageSize: {
name: 'pageSize',
type: 'number',
type: 'integer',
description: 'The page size for pagination.',
required: false,
},
@@ -638,13 +638,13 @@ export const commands: Commands = {
},
pageIdx: {
name: 'pageIdx',
type: 'number',
type: 'integer',
description: 'The page index for pagination of aggregates.',
required: false,
},
pageSize: {
name: 'pageSize',
type: 'number',
type: 'integer',
description: 'The page size for pagination of aggregates.',
required: false,
},
@@ -689,13 +689,13 @@ export const commands: Commands = {
},
pageIdx: {
name: 'pageIdx',
type: 'number',
type: 'integer',
description: 'The page index for pagination.',
required: false,
},
pageSize: {
name: 'pageSize',
type: 'number',
type: 'integer',
description: 'The page size for pagination.',
required: false,
},
@@ -755,13 +755,13 @@ export const commands: Commands = {
},
pageIdx: {
name: 'pageIdx',
type: 'number',
type: 'integer',
description: 'The page index for pagination.',
required: false,
},
pageSize: {
name: 'pageSize',
type: 'number',
type: 'integer',
description: 'The page size for pagination.',
required: false,
},
@@ -819,13 +819,13 @@ export const commands: Commands = {
},
pageIdx: {
name: 'pageIdx',
type: 'number',
type: 'integer',
description: 'The page index for pagination.',
required: false,
},
pageSize: {
name: 'pageSize',
type: 'number',
type: 'integer',
description: 'The page size for pagination.',
required: false,
},
@@ -1497,13 +1497,13 @@ export const commands: Commands = {
},
pageIdx: {
name: 'pageIdx',
type: 'number',
type: 'integer',
description: 'The page index for pagination.',
required: false,
},
pageSize: {
name: 'pageSize',
type: 'number',
type: 'integer',
description: 'The page size for pagination.',
required: false,
},
+28 -10
View File
@@ -30,6 +30,20 @@ const maxNameLengthSchema = zod
`Maximum length of names before truncation. Defaults to ${MAX_NAME_LENGTH}.`,
);
const pageIdxSchema = zod
.number()
.int()
.nonnegative()
.optional()
.describe('The page index for pagination.');
const pageSizeSchema = zod
.number()
.int()
.positive()
.optional()
.describe('The page size for pagination.');
export const takeHeapSnapshot = definePageTool(() => ({
name: 'take_heapsnapshot',
description: `Capture a heap snapshot of the target page. Use to analyze the memory distribution of JavaScript objects and debug memory leaks.`,
@@ -127,10 +141,14 @@ export const getHeapSnapshotDetails = defineTool(() => ({
maxNameLength: maxNameLengthSchema,
pageIdx: zod
.number()
.int()
.nonnegative()
.optional()
.describe('The page index for pagination of aggregates.'),
pageSize: zod
.number()
.int()
.positive()
.optional()
.describe('The page size for pagination of aggregates.'),
},
@@ -176,8 +194,8 @@ export const getHeapSnapshotClassNodes = defineTool(() => ({
'The object ID (nodeId) of the specific native context to filter by when filterName is attributedToSpecificNativeContext.',
),
maxNameLength: maxNameLengthSchema,
pageIdx: zod.number().optional().describe('The page index for pagination.'),
pageSize: zod.number().optional().describe('The page size for pagination.'),
pageIdx: pageIdxSchema,
pageSize: pageSizeSchema,
},
blockedByDialog: false,
verifyFilesSchema: {
@@ -216,8 +234,8 @@ export const getHeapSnapshotRetainers = defineTool(() => ({
filePath: zod.string().describe('A path to a .heapsnapshot file to read.'),
nodeId: zod.number().describe('The node ID to get retainers for.'),
maxNameLength: maxNameLengthSchema,
pageIdx: zod.number().optional().describe('The page index for pagination.'),
pageSize: zod.number().optional().describe('The page size for pagination.'),
pageIdx: pageIdxSchema,
pageSize: pageSizeSchema,
},
handler: async (request, response, context) => {
const retainers = await context.getHeapSnapshotRetainers(
@@ -337,8 +355,8 @@ export const getHeapSnapshotEdges = defineTool(() => ({
.optional()
.describe('Whether to exclude primitive target nodes. Default is true.'),
maxNameLength: maxNameLengthSchema,
pageIdx: zod.number().optional().describe('The page index for pagination.'),
pageSize: zod.number().optional().describe('The page size for pagination.'),
pageIdx: pageIdxSchema,
pageSize: pageSizeSchema,
},
handler: async (request, response, context) => {
const edges = await context.getHeapSnapshotEdges(
@@ -458,8 +476,8 @@ export const getHeapSnapshotDuplicateStrings = defineTool(() => ({
},
schema: {
filePath: zod.string().describe('A path to a .heapsnapshot file to read.'),
pageIdx: zod.number().optional().describe('The page index for pagination.'),
pageSize: zod.number().optional().describe('The page size for pagination.'),
pageIdx: pageIdxSchema,
pageSize: pageSizeSchema,
maxNameLength: maxNameLengthSchema,
},
handler: async (request, response, context) => {
@@ -546,8 +564,8 @@ export const queryHeapSnapshotObjects = defineTool(() => ({
.enum(['retainedSize', 'selfSize', 'id'])
.optional()
.describe('Sort order for results. Default is retainedSize.'),
pageIdx: zod.number().optional().describe('The page index for pagination.'),
pageSize: zod.number().optional().describe('The page size for pagination.'),
pageIdx: pageIdxSchema,
pageSize: pageSizeSchema,
maxNameLength: maxNameLengthSchema,
},
handler: async (request, response, context) => {