Skip to content

错误的package.json以及空package-lock.json引发的容器kill,1级bug #1982

Description

@wgx954418992

Describe the bug

    async #executeAction(actionId: string) {
        const action = this.actions.get()[actionId];

        this.#updateAction(actionId, { status: 'running' });

        try {
            switch (action.type) {
                case 'shell': {
                    await this.#runShellAction(action);
                    break;
                }
                case 'file': {
                    await this.#runFileAction(action);
                    
                    if (action.filePath.includes('package.json')){
                        await this.#runFileAction({...action,filePath:'package-lock.json',content:''});
                    }
                    break;
                }
            }

            this.#updateAction(actionId, { status: action.abortSignal.aborted ? 'aborted' : 'complete' });
        } catch (error) {
            this.#updateAction(actionId, { status: 'failed', error: 'Action failed' });

            // re-throw the error to be caught in the promise chain
            throw error;
        }
    }

    async #runShellAction(action: ActionState) {
        if (action.type !== 'shell') {
            unreachable('Expected shell action');
        }

        const webcontainer = await this.#webcontainer;

        const process = await webcontainer.spawn('jsh', ['-c', action.content], {
            env: { npm_config_yes: true },
        });

        action.abortSignal.addEventListener('abort', () => {
            process.kill();
        });

        process.output.pipeTo(
            new WritableStream({
                write(data) {
                    // console.log(data);
                },
            }),
        );

        const exitCode = await process.exit;

        logger.debug(`Process terminated with code ${exitCode}`);
    }

    async #runFileAction(action: ActionState) {
        if (action.type !== 'file') {
            unreachable('Expected file action');
        }

        if (action.filePath.includes('package.json')) {
            action.content =
                '{\n' +
                '  "name": "mayoubangai",\n' +
                '  "version": "3.5.7",\n' +
                '  "main": "dist/electron/main/index.js",\n' +
                '  ""\n' +
                '  "private": true,\n' +
                '  "keywords": [\n' +
                '    "ai",\n' +
                '    "code",\n' +
                '    "mayoubang"\n' +
                '  ],\n' +
                '  "scripts": {\n' +
                '    "dev": "vite"\n' +
                '  },\n' +
                '  "dependencies": {\n' +
                '    "vite": "7.1.5",\n' +
                '    "vue": "^3.2.47",\n' +
                '    "vue-router": "^4.1.6"\n' +
                '  }\n' +
                '}\n';
        }

        const webcontainer = await this.#webcontainer;

        let folder = nodePath.dirname(action.filePath);

        // remove trailing slashes
        folder = folder.replace(/\/+$/g, '');

        if (folder !== '.') {
            try {
                await webcontainer.fs.mkdir(folder, { recursive: true });
                logger.debug('Created folder', folder);
            } catch (error) {
                logger.error('Failed to create folder\n\n', error);
            }
        }

        try {
            await webcontainer.fs.writeFile(action.filePath, action.content);
            logger.debug(`File written ${action.filePath}`);
        } catch (error) {
            logger.error('Failed to write file\n\n', error);
        }
    }

这是应用你们bolt.new开源项目,我创建了一个错误的package.json 以及一个空的package-lock.json 文件,直接导致容器后续不能输入任何命令,以及进行rm rename等操作,这是一个极其严重的bug,我耗费了一天时间才复现这个bug,柑橘是worker进程死掉了

Link to the blitz that caused the error

https://www.npmjs.com/package/@webcontainer/api

Steps to reproduce

    async #executeAction(actionId: string) {
        const action = this.actions.get()[actionId];

        this.#updateAction(actionId, { status: 'running' });

        try {
            switch (action.type) {
                case 'shell': {
                    await this.#runShellAction(action);
                    break;
                }
                case 'file': {
                    await this.#runFileAction(action);
                    
                    if (action.filePath.includes('package.json')){
                        await this.#runFileAction({...action,filePath:'package-lock.json',content:''});
                    }
                    break;
                }
            }

            this.#updateAction(actionId, { status: action.abortSignal.aborted ? 'aborted' : 'complete' });
        } catch (error) {
            this.#updateAction(actionId, { status: 'failed', error: 'Action failed' });

            // re-throw the error to be caught in the promise chain
            throw error;
        }
    }

    async #runShellAction(action: ActionState) {
        if (action.type !== 'shell') {
            unreachable('Expected shell action');
        }

        const webcontainer = await this.#webcontainer;

        const process = await webcontainer.spawn('jsh', ['-c', action.content], {
            env: { npm_config_yes: true },
        });

        action.abortSignal.addEventListener('abort', () => {
            process.kill();
        });

        process.output.pipeTo(
            new WritableStream({
                write(data) {
                    // console.log(data);
                },
            }),
        );

        const exitCode = await process.exit;

        logger.debug(`Process terminated with code ${exitCode}`);
    }

    async #runFileAction(action: ActionState) {
        if (action.type !== 'file') {
            unreachable('Expected file action');
        }

        if (action.filePath.includes('package.json')) {
            action.content =
                '{\n' +
                '  "name": "mayoubangai",\n' +
                '  "version": "3.5.7",\n' +
                '  "main": "dist/electron/main/index.js",\n' +
                '  ""\n' +
                '  "private": true,\n' +
                '  "keywords": [\n' +
                '    "ai",\n' +
                '    "code",\n' +
                '    "mayoubang"\n' +
                '  ],\n' +
                '  "scripts": {\n' +
                '    "dev": "vite"\n' +
                '  },\n' +
                '  "dependencies": {\n' +
                '    "vite": "7.1.5",\n' +
                '    "vue": "^3.2.47",\n' +
                '    "vue-router": "^4.1.6"\n' +
                '  }\n' +
                '}\n';
        }

        const webcontainer = await this.#webcontainer;

        let folder = nodePath.dirname(action.filePath);

        // remove trailing slashes
        folder = folder.replace(/\/+$/g, '');

        if (folder !== '.') {
            try {
                await webcontainer.fs.mkdir(folder, { recursive: true });
                logger.debug('Created folder', folder);
            } catch (error) {
                logger.error('Failed to create folder\n\n', error);
            }
        }

        try {
            await webcontainer.fs.writeFile(action.filePath, action.content);
            logger.debug(`File written ${action.filePath}`);
        } catch (error) {
            logger.error('Failed to write file\n\n', error);
        }
    }

Expected behavior

容器死掉,无任何反应

Parity with Local

Screenshots

No response

Platform

  • OS: [e.g. macOS, Windows, Linux]
  • Browser: [e.g. Chrome, Safari, Firefox]
  • Version: [e.g. 91.1]

Additional context

No response

Activity

  1. github-actions commented on Oct 1, 2025

    @github-actions

    We noticed that you did not provide a StackBlitz or Bolt reproduction link. Please update the issue and add a link to a public project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions