33 *
44 * These functions perform I/O (filesystem, config reads) to detect security issues.
55 */
6- import fs from "node:fs/promises" ;
76import path from "node:path" ;
87import { clearTimeout as clearNodeTimeout , setTimeout as setNodeTimeout } from "node:timers" ;
98import {
@@ -21,6 +20,7 @@ import { MANIFEST_KEY } from "../compat/legacy-names.js";
2120import type { OpenClawConfig , ConfigFileSnapshot } from "../config/config.js" ;
2221import { collectIncludePathsRecursive } from "../config/includes-scan.js" ;
2322import { resolveOAuthDir } from "../config/paths.js" ;
23+ import { readRegularFile , statRegularFile } from "../infra/fs-safe.js" ;
2424import { normalizeAgentId } from "../routing/session-key.js" ;
2525import { createLazyRuntimeModule , createLazyRuntimeNamedExport } from "../shared/lazy-runtime.js" ;
2626import type { SkillScanFinding } from "../skills/security/scanner.js" ;
@@ -96,9 +96,25 @@ function expandTilde(p: string, env: NodeJS.ProcessEnv): string | null {
9696 return null ;
9797}
9898
99+ const MAX_PLUGIN_MANIFEST_BYTES = 1024 * 1024 ;
100+
99101async function readPluginManifestExtensions ( pluginPath : string ) : Promise < string [ ] > {
100102 const manifestPath = path . join ( pluginPath , "package.json" ) ;
101- const raw = await fs . readFile ( manifestPath , "utf-8" ) . catch ( ( ) => "" ) ;
103+ const statResult = await statRegularFile ( manifestPath ) ;
104+ if ( statResult . missing ) {
105+ return [ ] ;
106+ }
107+ if ( statResult . stat . size > MAX_PLUGIN_MANIFEST_BYTES ) {
108+ throw new Error (
109+ `Plugin manifest at ${ manifestPath } is too large (${ statResult . stat . size } bytes, max ${ MAX_PLUGIN_MANIFEST_BYTES } )` ,
110+ ) ;
111+ }
112+
113+ const { buffer } = await readRegularFile ( {
114+ filePath : manifestPath ,
115+ maxBytes : MAX_PLUGIN_MANIFEST_BYTES ,
116+ } ) ;
117+ const raw = buffer . toString ( "utf-8" ) ;
102118 if ( ! raw . trim ( ) ) {
103119 return [ ] ;
104120 }
0 commit comments