chrome-devtools-mcp 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,8 +4,9 @@
4
4
  * Copyright 2026 Google LLC
5
5
  * SPDX-License-Identifier: Apache-2.0
6
6
  */
7
- import fs from 'node:fs';
7
+ import fs, { constants, openSync, writeSync, closeSync } from 'node:fs';
8
8
  import { createServer } from 'node:net';
9
+ import os from 'node:os';
9
10
  import path from 'node:path';
10
11
  import process from 'node:process';
11
12
  import { logger } from '../logger.js';
@@ -19,10 +20,66 @@ if (isDaemonRunning(sessionId)) {
19
20
  process.exit(1);
20
21
  }
21
22
  const pidFilePath = getPidFilePath(sessionId);
22
- fs.mkdirSync(path.dirname(pidFilePath), {
23
- recursive: true,
24
- });
25
- fs.writeFileSync(pidFilePath, process.pid.toString());
23
+ const pidDir = path.dirname(pidFilePath);
24
+ const currentUserUid = os.userInfo().uid;
25
+ try {
26
+ fs.mkdirSync(pidDir, { recursive: true });
27
+ if (os.platform() !== 'win32') {
28
+ // POSIX specific checks
29
+ try {
30
+ const stats = fs.statSync(pidDir);
31
+ // 1. Check Ownership: Ensure the directory is owned by the current user.
32
+ if (stats.uid !== currentUserUid) {
33
+ console.error(`[MCP Daemon] Critical error: PID directory ${pidDir} is not owned by the current user (Expected: ${currentUserUid}, Found: ${stats.uid}). Possible tampering.`);
34
+ process.exit(1);
35
+ }
36
+ // 2. Check Permissions: Ensure the directory is not group or world-writable.
37
+ // Mode is a number, e.g., 0o700. We check if bits for group/world write are set.
38
+ const mode = stats.mode;
39
+ if (mode & constants.S_IWGRP || mode & constants.S_IWOTH) {
40
+ console.error(`[MCP Daemon] Critical error: PID directory ${pidDir} has insecure permissions (Mode: ${mode.toString(8)}). It should not be writable by group or others.`);
41
+ process.exit(1);
42
+ }
43
+ }
44
+ catch (statErr) {
45
+ console.error(`[MCP Daemon] Critical error stating PID directory ${pidDir}:`, statErr);
46
+ process.exit(1);
47
+ }
48
+ }
49
+ }
50
+ catch (err) {
51
+ console.error(`[MCP Daemon] Critical error creating/validating PID directory: ${pidDir}`, err);
52
+ process.exit(1);
53
+ }
54
+ let fd = -1;
55
+ try {
56
+ // Open the file with flags to:
57
+ // - O_WRONLY: Write-only
58
+ // - O_CREAT: Create if it doesn't exist
59
+ // - O_TRUNC: Truncate to zero length if it exists
60
+ // - O_NOFOLLOW: DO NOT follow symlinks.
61
+ // - 0o600: Permissions: read/write for owner, no permissions for others.
62
+ fd = openSync(pidFilePath, constants.O_WRONLY |
63
+ constants.O_CREAT |
64
+ constants.O_TRUNC |
65
+ constants.O_NOFOLLOW, 0o600);
66
+ writeSync(fd, process.pid.toString());
67
+ }
68
+ catch (err) {
69
+ console.error(`[MCP Daemon] Critical error writing PID file: ${pidFilePath}`, err);
70
+ // If openSync fails due to O_NOFOLLOW on a symlink, the error will be caught here.
71
+ process.exit(1);
72
+ }
73
+ finally {
74
+ if (fd !== -1) {
75
+ try {
76
+ closeSync(fd);
77
+ }
78
+ catch (err) {
79
+ console.error(`[MCP Daemon] Error closing PID file: ${pidFilePath}`, err);
80
+ }
81
+ }
82
+ }
26
83
  logger(`Writing ${process.pid.toString()} to ${pidFilePath}`);
27
84
  const socketPath = getSocketPath(sessionId);
28
85
  const startDate = new Date();
@@ -29,10 +29,10 @@ export class HeapSnapshotFormatter {
29
29
  if (items.length > 0) {
30
30
  const firstItem = items[0];
31
31
  if (isNodeLike(firstItem)) {
32
- lines.push('id,name,type,distance,selfSize,retainedSize');
32
+ lines.push('nodeId,nodeName,type,distance,selfSize,retainedSize');
33
33
  }
34
34
  else if (isEdgeLike(firstItem)) {
35
- lines.push('edgeIndex,edgeName,edgeType,targetNodeId,targetNodeName');
35
+ lines.push('name,type,nodeId,nodeName');
36
36
  }
37
37
  }
38
38
  for (const item of items) {
@@ -40,7 +40,7 @@ export class HeapSnapshotFormatter {
40
40
  lines.push(`${item.id},${item.name},${item.type},${item.distance},${DevTools.I18n.ByteUtilities.formatBytesToKb(item.selfSize)},${DevTools.I18n.ByteUtilities.formatBytesToKb(item.retainedSize)}`);
41
41
  }
42
42
  else if (isEdgeLike(item)) {
43
- lines.push(`${item.edgeIndex},${item.name},${item.type},${item.node.id},${item.node.name}`);
43
+ lines.push(`${item.name},${item.type},${item.node.id},${item.node.name}`);
44
44
  }
45
45
  }
46
46
  return lines.join('\n');
@@ -51,17 +51,17 @@ export class HeapSnapshotFormatter {
51
51
  toString() {
52
52
  const sorted = this.#getSortedAggregates();
53
53
  const lines = [];
54
- lines.push('uid,className,count,selfSize,maxRetainedSize');
54
+ lines.push('id,name,count,selfSize,maxRetainedSize');
55
55
  for (const info of sorted) {
56
- const uid = info[stableIdSymbol] ?? '';
57
- lines.push(`${uid},${info.name},${info.count},${DevTools.I18n.ByteUtilities.formatBytesToKb(info.self)},${DevTools.I18n.ByteUtilities.formatBytesToKb(info.maxRet)}`);
56
+ const id = info[stableIdSymbol] ?? '';
57
+ lines.push(`${id},${info.name},${info.count},${DevTools.I18n.ByteUtilities.formatBytesToKb(info.self)},${DevTools.I18n.ByteUtilities.formatBytesToKb(info.maxRet)}`);
58
58
  }
59
59
  return lines.join('\n');
60
60
  }
61
61
  toJSON() {
62
62
  const sorted = this.#getSortedAggregates();
63
63
  return sorted.map(info => ({
64
- uid: info[stableIdSymbol],
64
+ id: info[stableIdSymbol],
65
65
  className: info.name,
66
66
  count: info.count,
67
67
  selfSize: DevTools.I18n.ByteUtilities.formatBytesToKb(info.self),
@@ -324,7 +324,7 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH RE
324
324
 
325
325
  Name: semver
326
326
  URL: git+https://github.com/npm/node-semver.git
327
- Version: 7.8.0
327
+ Version: 7.8.1
328
328
  License: ISC
329
329
 
330
330
  The ISC License
@@ -867,21 +867,21 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
867
867
 
868
868
  Name: puppeteer-core
869
869
  URL: https://github.com/puppeteer/puppeteer/tree/main/packages/puppeteer-core
870
- Version: 25.0.4
870
+ Version: 25.1.0
871
871
  License: Apache-2.0
872
872
 
873
873
  -------------------- DEPENDENCY DIVIDER --------------------
874
874
 
875
875
  Name: @puppeteer/browsers
876
876
  URL: https://github.com/puppeteer/puppeteer/tree/main/packages/browsers
877
- Version: 3.0.3
877
+ Version: 3.0.4
878
878
  License: Apache-2.0
879
879
 
880
880
  -------------------- DEPENDENCY DIVIDER --------------------
881
881
 
882
882
  Name: ws
883
883
  URL: https://github.com/websockets/ws
884
- Version: 8.20.0
884
+ Version: 8.21.0
885
885
  License: MIT
886
886
 
887
887
  Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "@modelcontextprotocol/sdk": "1.29.0",
3
- "chrome-devtools-frontend": "1.0.1631386",
3
+ "chrome-devtools-frontend": "1.0.1632065",
4
4
  "core-js": "3.49.0",
5
5
  "debug": "4.4.3",
6
6
  "lighthouse": "13.3.0",
7
7
  "semver": "^7.7.4",
8
8
  "urlpattern-polyfill": "^10.1.0",
9
9
  "yargs": "18.0.0",
10
- "puppeteer-core": "25.0.4"
10
+ "puppeteer-core": "25.1.0"
11
11
  }
@@ -12262,6 +12262,7 @@ const UIStrings$2 = {
12262
12262
  memoryInspectorPanel: 'Memory inspector panel',
12263
12263
  developerResourcesPanel: 'Developer Resources panel',
12264
12264
  animationsPanel: 'Animations panel',
12265
+ lighthousePanel: 'Lighthouse panel',
12265
12266
  };
12266
12267
  const str_$2 = registerUIStrings('core/common/Revealer.ts', UIStrings$2);
12267
12268
  const i18nLazyString$1 = getLazilyComputedLocalizedString.bind(undefined, str_$2);
@@ -12278,6 +12279,7 @@ const i18nLazyString$1 = getLazilyComputedLocalizedString.bind(undefined, str_$2
12278
12279
  SOURCES_PANEL: i18nLazyString$1(UIStrings$2.sourcesPanel),
12279
12280
  MEMORY_INSPECTOR_PANEL: i18nLazyString$1(UIStrings$2.memoryInspectorPanel),
12280
12281
  ANIMATIONS_PANEL: i18nLazyString$1(UIStrings$2.animationsPanel),
12282
+ LIGHTHOUSE_PANEL: i18nLazyString$1(UIStrings$2.lighthousePanel),
12281
12283
  });
12282
12284
 
12283
12285
  // Copyright 2014 The Chromium Authors
@@ -4808,6 +4808,7 @@ const UIStrings$2 = {
4808
4808
  memoryInspectorPanel: 'Memory inspector panel',
4809
4809
  developerResourcesPanel: 'Developer Resources panel',
4810
4810
  animationsPanel: 'Animations panel',
4811
+ lighthousePanel: 'Lighthouse panel',
4811
4812
  };
4812
4813
  const str_$2 = registerUIStrings('core/common/Revealer.ts', UIStrings$2);
4813
4814
  const i18nLazyString$1 = getLazilyComputedLocalizedString.bind(undefined, str_$2);
@@ -4863,6 +4864,7 @@ async function reveal(revealable, omitFocus = false) {
4863
4864
  SOURCES_PANEL: i18nLazyString$1(UIStrings$2.sourcesPanel),
4864
4865
  MEMORY_INSPECTOR_PANEL: i18nLazyString$1(UIStrings$2.memoryInspectorPanel),
4865
4866
  ANIMATIONS_PANEL: i18nLazyString$1(UIStrings$2.animationsPanel),
4867
+ LIGHTHOUSE_PANEL: i18nLazyString$1(UIStrings$2.lighthousePanel),
4866
4868
  });
4867
4869
 
4868
4870
  // Copyright 2014 The Chromium Authors