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.

@middleware.io/node-apm

↗️ View on Npm
⚠️
Found 26 vulnerable versions for package 
@middleware.io/node-apm
:

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

The code reads a configuration file from the local file system that may contain sensitive information such as an API key and then sends this information to a remote server via an HTTPS POST request. This could potentially expose sensitive data and leads to unauthorized access if the server is malicious or compromised. Additionally, the use of rejectUnauthorized: false can make the connection susceptible to man-in-the-middle attacks, allowing for the interception of sensitive information.

Install script:
node scripts/postinstall.js
Install script code:
const path = require("path");
const fs = require("fs");
const os = require("os");
const https = require('https')

const API_URI = 'api/v1/apm/tracking'
const configPath = path.join("/etc", "mw-agent", "agent-config.yaml");

function readAgentConfig(configPath) {
  try {
    // Read the file content

    if (fs.existsSync(configPath)) {
      const fileContent = fs.readFileSync(configPath, "utf8");

      // Split into lines and process
      const lines = fileContent.split("\n");

      let apiKey = "";
      let target = "";

      for (const line of lines) {
        // Skip comments and empty lines
        if (line.trim().startsWith("#") || !line.trim()) {
          continue;
        }

        // Look for api-key
        if (line.includes("api-key:")) {
          apiKey = line.split("api-key:")[1].trim();
        }

        // Look for target
        if (line.includes("target:")) {
          target = line
            .split("target:")[1]
            .trim();
        }
      }

      return {
        apiKey,
        target,
      };
    } else {
      console.error(`Config file not found `);
    }
  } catch (error) {
    console.error("Error reading config file:", error);
  }

  return null;
}

async function trackPostInstall(config) {

  const payload = {
    status: "apm_installed",
    metadata: {
      host_id: os.hostname(),
      os_type: os.platform(),
      apm_type: "Node",
      apm_data: {
        script: 'npm-install',
        os_version: os.release(),
        node_version: process.version,
        package_name: process.env.npm_package_name,
        package_version: process.env.npm_package_version,
        npm_lifecycle_event: process.env.npm_lifecycle_event,
        reason: 'PostInstall tracking'
      }
    }
  };

  const data = JSON.stringify(payload);
  const baseUrl = new URL(config.target);
  const pathSuffix = `${API_URI}/${config.apiKey}`;
  if (!baseUrl.pathname.endsWith('/')) {
    baseUrl.pathname += '/';
  }
  baseUrl.pathname += pathSuffix;

  const options = {
    method: 'POST',
    hostname: baseUrl.hostname,
    path: baseUrl.pathname,
    port: 443,
    headers: {
      'Content-Type': 'application/json',
      //'Content-Length': Buffer.byteLength(data)
    },
    rejectUnauthorized: false,
  };

  await makeRequest(options, data);
  console.log(`Successfully tracked event`);
}

async function makeRequest(options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let responseData = '';
      res.on('data', (chunk) => responseData += chunk);
      res.on('end', () => resolve(responseData));
    });

    req.on('error', (error) => {
      console.log(error)
      console.log('Warning: Request failed', error.message);
      resolve();
    });

    req.setTimeout(5000, () => {
      req.destroy();
      resolve();
    });

    req.write(data);
    req.end();
  });
}
try {
  (async () => {
    try {
      const config = readAgentConfig(configPath);

      if (!config || !config.apiKey || !config.target) {
        console.error("Invalid configuration: Missing API key or URL");
        return; // Return instead of throw
      }
      await trackPostInstall(config);
    } catch (error) {
      console.error("Pre-install script failed", error);
    } finally {
      process.exit(0);
    }
  })();
} catch (error) {
  console.error("Fatal error:", error);
  process.exit(0); // Still exit with 0 to not break npm install
}


// Ensure we always exit safely, even if there's an uncaught error
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  process.exit(0);
});

// Ensure we always exit safely, even if there's an unhandled rejection
process.on('unhandledRejection', (error) => {
  console.error('Unhandled Rejection:', error);
  process.exit(0);
});

// Set a timeout to ensure the script doesn't hang
setTimeout(() => {
  console.log('Script timeout reached, exiting safely');
  process.exit(0);
}, 10000);

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

The script reads a configuration file for an API key and a target URL, then sends sensitive information about the system back to a remote server using an HTTPS POST request. This poses a risk as it may expose sensitive details, including the API key, to potential attackers if the target is malicious. Furthermore, it accepts a target URL from the configuration, which can lead to issues like sending data to unintended or harmful servers. The script also has vulnerabilities like disabling SSL verification (rejectUnauthorized: false), which can expose it to man-in-the-middle attacks.

Install script:
node scripts/postinstall.js
Install script code:
const path = require("path");
const fs = require("fs");
const os = require("os");
const https = require('https')

const API_URI = 'api/v1/apm/tracking'
const configPath = path.join("/etc", "mw-agent", "agent-config.yaml");

function readAgentConfig(configPath) {
  try {
    // Read the file content

    if (fs.existsSync(configPath)) {
      const fileContent = fs.readFileSync(configPath, "utf8");

      // Split into lines and process
      const lines = fileContent.split("\n");

      let apiKey = "";
      let target = "";

      for (const line of lines) {
        // Skip comments and empty lines
        if (line.trim().startsWith("#") || !line.trim()) {
          continue;
        }

        // Look for api-key
        if (line.includes("api-key:")) {
          apiKey = line.split("api-key:")[1].trim();
        }

        // Look for target
        if (line.includes("target:")) {
          target = line
            .split("target:")[1]
            .trim();
        }
      }

      return {
        apiKey,
        target,
      };
    } else {
      console.error(`Config file not found `);
    }
  } catch (error) {
    console.error("Error reading config file:", error);
  }

  return null;
}

async function trackPostInstall(config) {

  const payload = {
    status: "apm_installed",
    metadata: {
      host_id: os.hostname(),
      os_type: os.platform(),
      apm_type: "Node",
      apm_data: {
        script: 'npm-install',
        os_version: os.release(),
        node_version: process.version,
        package_name: process.env.npm_package_name,
        package_version: process.env.npm_package_version,
        npm_lifecycle_event: process.env.npm_lifecycle_event,
        reason: 'PostInstall tracking'
      }
    }
  };

  const data = JSON.stringify(payload);
  const baseUrl = new URL(config.target);
  const pathSuffix = `${API_URI}/${config.apiKey}`;
  if (!baseUrl.pathname.endsWith('/')) {
    baseUrl.pathname += '/';
  }
  baseUrl.pathname += pathSuffix;

  const options = {
    method: 'POST',
    hostname: baseUrl.hostname,
    path: baseUrl.pathname,
    port: 443,
    headers: {
      'Content-Type': 'application/json',
      //'Content-Length': Buffer.byteLength(data)
    },
    rejectUnauthorized: false,
  };

  await makeRequest(options, data);
  console.log(`Successfully tracked event`);
}

async function makeRequest(options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let responseData = '';
      res.on('data', (chunk) => responseData += chunk);
      res.on('end', () => resolve(responseData));
    });

    req.on('error', (error) => {
      console.log(error)
      console.log('Warning: Request failed', error.message);
      resolve();
    });

    req.setTimeout(5000, () => {
      req.destroy();
      resolve();
    });

    req.write(data);
    req.end();
  });
}
try {
  (async () => {
    try {
      const config = readAgentConfig(configPath);

      if (!config || !config.apiKey || !config.target) {
        console.error("Invalid configuration: Missing API key or URL");
        return; // Return instead of throw
      }
      await trackPostInstall(config);
    } catch (error) {
      console.error("Pre-install script failed", error);
    } finally {
      process.exit(0);
    }
  })();
} catch (error) {
  console.error("Fatal error:", error);
  process.exit(0); // Still exit with 0 to not break npm install
}


// Ensure we always exit safely, even if there's an uncaught error
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  process.exit(0);
});

// Ensure we always exit safely, even if there's an unhandled rejection
process.on('unhandledRejection', (error) => {
  console.error('Unhandled Rejection:', error);
  process.exit(0);
});

// Set a timeout to ensure the script doesn't hang
setTimeout(() => {
  console.log('Script timeout reached, exiting safely');
  process.exit(0);
}, 10000);

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

The script reads sensitive data, specifically an API key, from a configuration file located in a standard system directory (/etc/mw-agent/agent-config.yaml). It sends this data to a remote server using HTTP over the internet. This behavior can lead to exposure of sensitive information if the server is compromised or malicious, making it a potential vector for data theft or unauthorized access.

Install script:
node scripts/preinstall.js
Install script code:
const path = require("path");
const fs = require("fs");
const os = require("os");
const https = require('https')

const API_URI = 'api/v1/apm/tracking'
const configPath = path.join("/etc", "mw-agent", "agent-config.yaml");

function readAgentConfig(configPath) {
  try {
    // Read the file content

    if (fs.existsSync(configPath)) {
      const fileContent = fs.readFileSync(configPath, "utf8");

      // Split into lines and process
      const lines = fileContent.split("\n");

      let apiKey = "";
      let target = "";

      for (const line of lines) {
        // Skip comments and empty lines
        if (line.trim().startsWith("#") || !line.trim()) {
          continue;
        }

        // Look for api-key
        if (line.includes("api-key:")) {
          apiKey = line.split("api-key:")[1].trim();
        }

        // Look for target
        if (line.includes("target:")) {
          target = line
            .split("target:")[1]
            .trim();
        }
      }

      return {
        apiKey,
        target,
      };
    } else {
      console.error(`Config file not found `);
    }
  } catch (error) {
    console.error("Error reading config file:", error);
    //throw error;
  }

  return null;
}

async function trackPreInstall(config) {

  const payload = {
    status: "apm_tried",
    metadata: {
      host_id: os.hostname(),
      os_type: os.platform(),
      apm_type: "Node",
      apm_data: {
        script: 'npm-install',
        os_version: os.release(),
        node_version: process.version,
        package_name: process.env.npm_package_name,
        package_version: process.env.npm_package_version,
        npm_lifecycle_event: process.env.npm_lifecycle_event,
        reason: 'PreInstall tracking'
      }
    }
  };

  const data = JSON.stringify(payload);
  const baseUrl = new URL(config.target);
  const pathSuffix = `${API_URI}/${config.apiKey}`;
  if (!baseUrl.pathname.endsWith('/')) {
    baseUrl.pathname += '/';
  }
  baseUrl.pathname += pathSuffix;

  const options = {
    method: 'POST',
    hostname: baseUrl.hostname,
    path: baseUrl.pathname,
    port: 443,
    headers: {
      'Content-Type': 'application/json',
    },
    rejectUnauthorized: false,
  };

  await makeRequest(options, data);
  console.log(`Successfully tracked event`);
}

async function makeRequest(options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let responseData = '';
      res.on('data', (chunk) => responseData += chunk);
      res.on('end', () => resolve(responseData));
    });

    req.on('error', (error) => {
      console.log(error)
      console.log('Warning: Request failed', error.message);
      resolve();
    });

    req.setTimeout(5000, () => {
      req.destroy();
      resolve();
    });

    req.write(data);
    req.end();
  });
}
try {
  (async () => {
    try {
      const config = readAgentConfig(configPath);

      if (!config || !config.apiKey || !config.target) {
        console.error("Invalid configuration: Missing API key or URL");
        return; // Return instead of throw
      }
      await trackPreInstall(config);
    } catch (error) {
      console.error("Pre-install script failed", error);
    } finally {
      process.exit(0);
    }
  })();
} catch (error) {
  console.error("Fatal error:", error);
  process.exit(0); // Still exit with 0 to not break npm install
}


// Ensure we always exit safely, even if there's an uncaught error
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  process.exit(0);
});

// Ensure we always exit safely, even if there's an unhandled rejection
process.on('unhandledRejection', (error) => {
  console.error('Unhandled Rejection:', error);
  process.exit(0);
});

// Set a timeout to ensure the script doesn't hang
setTimeout(() => {
  console.log('Script timeout reached, exiting safely');
  process.exit(0);
}, 10000);



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

The code reads a configuration file that may contain sensitive information, such as an API key, and sends a POST request to a remote server with this information. It uses rejectUnauthorized: false, which allows for man-in-the-middle attacks by not verifying the server's SSL certificate. This could lead to sensitive data being intercepted or malicious code being executed on the target system.

Install script:
node scripts/postinstall.js
Install script code:
const path = require("path");
const fs = require("fs");
const os = require("os");
const https = require('https')

const API_URI = 'api/v1/apm/tracking'
const configPath = path.join("/etc", "mw-agent", "agent-config.yaml");

function readAgentConfig(configPath) {
  try {
    // Read the file content

    if (fs.existsSync(configPath)) {
      const fileContent = fs.readFileSync(configPath, "utf8");

      // Split into lines and process
      const lines = fileContent.split("\n");

      let apiKey = "";
      let target = "";

      for (const line of lines) {
        // Skip comments and empty lines
        if (line.trim().startsWith("#") || !line.trim()) {
          continue;
        }

        // Look for api-key
        if (line.includes("api-key:")) {
          apiKey = line.split("api-key:")[1].trim();
        }

        // Look for target
        if (line.includes("target:")) {
          target = line
            .split("target:")[1]
            .trim();
        }
      }

      return {
        apiKey,
        target,
      };
    } else {
      console.error(`Config file not found `);
    }
  } catch (error) {
    console.error("Error reading config file:", error);
  }

  return null;
}

async function trackPostInstall(config) {

  const payload = {
    status: "apm_installed",
    metadata: {
      host_id: os.hostname(),
      os_type: os.platform(),
      apm_type: "Node",
      apm_data: {
        script: 'npm-install',
        os_version: os.release(),
        node_version: process.version,
        package_name: process.env.npm_package_name,
        package_version: process.env.npm_package_version,
        npm_lifecycle_event: process.env.npm_lifecycle_event,
        reason: 'PostInstall tracking'
      }
    }
  };

  const data = JSON.stringify(payload);
  const baseUrl = new URL(config.target);
  const pathSuffix = `${API_URI}/${config.apiKey}`;
  if (!baseUrl.pathname.endsWith('/')) {
    baseUrl.pathname += '/';
  }
  baseUrl.pathname += pathSuffix;

  const options = {
    method: 'POST',
    hostname: baseUrl.hostname,
    path: baseUrl.pathname,
    port: 443,
    headers: {
      'Content-Type': 'application/json',
      //'Content-Length': Buffer.byteLength(data)
    },
    rejectUnauthorized: false,
  };

  await makeRequest(options, data);
  console.log(`Successfully tracked event`);
}

async function makeRequest(options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let responseData = '';
      res.on('data', (chunk) => responseData += chunk);
      res.on('end', () => resolve(responseData));
    });

    req.on('error', (error) => {
      console.log(error)
      console.log('Warning: Request failed', error.message);
      resolve();
    });

    req.setTimeout(5000, () => {
      req.destroy();
      resolve();
    });

    req.write(data);
    req.end();
  });
}
try {
  (async () => {
    try {
      const config = readAgentConfig(configPath);

      if (!config || !config.apiKey || !config.target) {
        console.error("Invalid configuration: Missing API key or URL");
        return; // Return instead of throw
      }
      await trackPostInstall(config);
    } catch (error) {
      console.error("Pre-install script failed", error);
    } finally {
      process.exit(0);
    }
  })();
} catch (error) {
  console.error("Fatal error:", error);
  process.exit(0); // Still exit with 0 to not break npm install
}


// Ensure we always exit safely, even if there's an uncaught error
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  process.exit(0);
});

// Ensure we always exit safely, even if there's an unhandled rejection
process.on('unhandledRejection', (error) => {
  console.error('Unhandled Rejection:', error);
  process.exit(0);
});

// Set a timeout to ensure the script doesn't hang
setTimeout(() => {
  console.log('Script timeout reached, exiting safely');
  process.exit(0);
}, 10000);

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

This script reads sensitive information such as an API key and a target URL from a configuration file, potentially exposing this information if the file is compromised. It then sends this information to a remote server via an HTTPS request. The use of rejectUnauthorized: false can expose the connection to man-in-the-middle attacks, allowing an attacker to intercept the data being sent, including sensitive information. Additionally, the script does not properly handle situations where configurations might be missing, leading to possible silent failures.

Install script:
node scripts/preinstall.js
Install script code:
const path = require("path");
const fs = require("fs");
const os = require("os");
const https = require('https')

const API_URI = 'api/v1/apm/tracking'
const configPath = path.join("/etc", "mw-agent", "agent-config.yaml");

function readAgentConfig(configPath) {
  try {
    // Read the file content

    if (fs.existsSync(configPath)) {
      const fileContent = fs.readFileSync(configPath, "utf8");

      // Split into lines and process
      const lines = fileContent.split("\n");

      let apiKey = "";
      let target = "";

      for (const line of lines) {
        // Skip comments and empty lines
        if (line.trim().startsWith("#") || !line.trim()) {
          continue;
        }

        // Look for api-key
        if (line.includes("api-key:")) {
          apiKey = line.split("api-key:")[1].trim();
        }

        // Look for target
        if (line.includes("target:")) {
          target = line
            .split("target:")[1]
            .trim();
        }
      }

      return {
        apiKey,
        target,
      };
    } else {
      console.error(`Config file not found `);
    }
  } catch (error) {
    console.error("Error reading config file:", error);
    //throw error;
  }

  return null;
}

async function trackPreInstall(config) {

  const payload = {
    status: "apm_tried",
    metadata: {
      host_id: os.hostname(),
      os_type: os.platform(),
      apm_type: "Node",
      apm_data: {
        script: 'npm-install',
        os_version: os.release(),
        node_version: process.version,
        package_name: process.env.npm_package_name,
        package_version: process.env.npm_package_version,
        npm_lifecycle_event: process.env.npm_lifecycle_event,
        reason: 'PreInstall tracking'
      }
    }
  };

  const data = JSON.stringify(payload);
  const baseUrl = new URL(config.target);
  const pathSuffix = `${API_URI}/${config.apiKey}`;
  if (!baseUrl.pathname.endsWith('/')) {
    baseUrl.pathname += '/';
  }
  baseUrl.pathname += pathSuffix;

  const options = {
    method: 'POST',
    hostname: baseUrl.hostname,
    path: baseUrl.pathname,
    port: 443,
    headers: {
      'Content-Type': 'application/json',
    },
    rejectUnauthorized: false,
  };

  await makeRequest(options, data);
  console.log(`Successfully tracked event`);
}

async function makeRequest(options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let responseData = '';
      res.on('data', (chunk) => responseData += chunk);
      res.on('end', () => resolve(responseData));
    });

    req.on('error', (error) => {
      console.log(error)
      console.log('Warning: Request failed', error.message);
      resolve();
    });

    req.setTimeout(5000, () => {
      req.destroy();
      resolve();
    });

    req.write(data);
    req.end();
  });
}
try {
  (async () => {
    try {
      const config = readAgentConfig(configPath);

      if (!config || !config.apiKey || !config.target) {
        console.error("Invalid configuration: Missing API key or URL");
        return; // Return instead of throw
      }
      await trackPreInstall(config);
    } catch (error) {
      console.error("Pre-install script failed", error);
    } finally {
      process.exit(0);
    }
  })();
} catch (error) {
  console.error("Fatal error:", error);
  process.exit(0); // Still exit with 0 to not break npm install
}


// Ensure we always exit safely, even if there's an uncaught error
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  process.exit(0);
});

// Ensure we always exit safely, even if there's an unhandled rejection
process.on('unhandledRejection', (error) => {
  console.error('Unhandled Rejection:', error);
  process.exit(0);
});

// Set a timeout to ensure the script doesn't hang
setTimeout(() => {
  console.log('Script timeout reached, exiting safely');
  process.exit(0);
}, 10000);



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

The script reads sensitive information such as an API key from a configuration file and sends it over an HTTPS request to a target URL specified in that file. This behavior, combined with the lack of certificate validation (rejectUnauthorized: false), poses a significant risk as it could facilitate man-in-the-middle attacks, allowing an attacker to intercept sensitive data without proper validation of the target server's identity.

Install script:
node scripts/preinstall.js
Install script code:
const path = require("path");
const fs = require("fs");
const os = require("os");
const https = require('https')

const API_URI = 'api/v1/apm/tracking'
const configPath = path.join("/etc", "mw-agent", "agent-config.yaml");

function readAgentConfig(configPath) {
  try {
    // Read the file content

    if (fs.existsSync(configPath)) {
      const fileContent = fs.readFileSync(configPath, "utf8");

      // Split into lines and process
      const lines = fileContent.split("\n");

      let apiKey = "";
      let target = "";

      for (const line of lines) {
        // Skip comments and empty lines
        if (line.trim().startsWith("#") || !line.trim()) {
          continue;
        }

        // Look for api-key
        if (line.includes("api-key:")) {
          apiKey = line.split("api-key:")[1].trim();
        }

        // Look for target
        if (line.includes("target:")) {
          target = line
            .split("target:")[1]
            .trim();
        }
      }

      return {
        apiKey,
        target,
      };
    } else {
      console.error(`Config file not found `);
    }
  } catch (error) {
    console.error("Error reading config file:", error);
    //throw error;
  }

  return null;
}

async function trackPreInstall(config) {

  const payload = {
    status: "apm_tried",
    metadata: {
      host_id: os.hostname(),
      os_type: os.platform(),
      apm_type: "Node",
      apm_data: {
        script: 'npm-install',
        os_version: os.release(),
        node_version: process.version,
        package_name: process.env.npm_package_name,
        package_version: process.env.npm_package_version,
        npm_lifecycle_event: process.env.npm_lifecycle_event,
        reason: 'PreInstall tracking'
      }
    }
  };

  const data = JSON.stringify(payload);
  const baseUrl = new URL(config.target);
  const pathSuffix = `${API_URI}/${config.apiKey}`;
  if (!baseUrl.pathname.endsWith('/')) {
    baseUrl.pathname += '/';
  }
  baseUrl.pathname += pathSuffix;

  const options = {
    method: 'POST',
    hostname: baseUrl.hostname,
    path: baseUrl.pathname,
    port: 443,
    headers: {
      'Content-Type': 'application/json',
    },
    rejectUnauthorized: false,
  };

  await makeRequest(options, data);
  console.log(`Successfully tracked event`);
}

async function makeRequest(options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let responseData = '';
      res.on('data', (chunk) => responseData += chunk);
      res.on('end', () => resolve(responseData));
    });

    req.on('error', (error) => {
      console.log(error)
      console.log('Warning: Request failed', error.message);
      resolve();
    });

    req.setTimeout(5000, () => {
      req.destroy();
      resolve();
    });

    req.write(data);
    req.end();
  });
}
try {
  (async () => {
    try {
      const config = readAgentConfig(configPath);

      if (!config || !config.apiKey || !config.target) {
        console.error("Invalid configuration: Missing API key or URL");
        return; // Return instead of throw
      }
      await trackPreInstall(config);
    } catch (error) {
      console.error("Pre-install script failed", error);
    } finally {
      process.exit(0);
    }
  })();
} catch (error) {
  console.error("Fatal error:", error);
  process.exit(0); // Still exit with 0 to not break npm install
}


// Ensure we always exit safely, even if there's an uncaught error
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  process.exit(0);
});

// Ensure we always exit safely, even if there's an unhandled rejection
process.on('unhandledRejection', (error) => {
  console.error('Unhandled Rejection:', error);
  process.exit(0);
});

// Set a timeout to ensure the script doesn't hang
setTimeout(() => {
  console.log('Script timeout reached, exiting safely');
  process.exit(0);
}, 10000);



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

The script reads a configuration file from a sensitive location, /etc/mw-agent/agent-config.yaml, which potentially contains an API key and a target URL for making a POST request to an external endpoint. This could lead to the exposure of sensitive information if the configuration file is compromised. Additionally, the URL is derived from user-defined input, and the rejectUnauthorized option is set to false, allowing for man-in-the-middle attacks and the risk of connecting to untrusted or malicious servers.

Install script:
node scripts/postinstall.js
Install script code:
const path = require("path");
const fs = require("fs");
const os = require("os");
const https = require('https')

const API_URI = 'api/v1/apm/tracking'
const configPath = path.join("/etc", "mw-agent", "agent-config.yaml");

function readAgentConfig(configPath) {
  try {
    // Read the file content

    if (fs.existsSync(configPath)) {
      const fileContent = fs.readFileSync(configPath, "utf8");

      // Split into lines and process
      const lines = fileContent.split("\n");

      let apiKey = "";
      let target = "";

      for (const line of lines) {
        // Skip comments and empty lines
        if (line.trim().startsWith("#") || !line.trim()) {
          continue;
        }

        // Look for api-key
        if (line.includes("api-key:")) {
          apiKey = line.split("api-key:")[1].trim();
        }

        // Look for target
        if (line.includes("target:")) {
          target = line
            .split("target:")[1]
            .trim();
        }
      }

      return {
        apiKey,
        target,
      };
    } else {
      console.error(`Config file not found `);
    }
  } catch (error) {
    console.error("Error reading config file:", error);
  }

  return null;
}

async function trackPostInstall(config) {

  const payload = {
    status: "apm_installed",
    metadata: {
      host_id: os.hostname(),
      os_type: os.platform(),
      apm_type: "Node",
      apm_data: {
        script: 'npm-install',
        os_version: os.release(),
        node_version: process.version,
        package_name: process.env.npm_package_name,
        package_version: process.env.npm_package_version,
        npm_lifecycle_event: process.env.npm_lifecycle_event,
        reason: 'PostInstall tracking'
      }
    }
  };

  const data = JSON.stringify(payload);
  const baseUrl = new URL(config.target);
  const pathSuffix = `${API_URI}/${config.apiKey}`;
  if (!baseUrl.pathname.endsWith('/')) {
    baseUrl.pathname += '/';
  }
  baseUrl.pathname += pathSuffix;

  const options = {
    method: 'POST',
    hostname: baseUrl.hostname,
    path: baseUrl.pathname,
    port: 443,
    headers: {
      'Content-Type': 'application/json',
      //'Content-Length': Buffer.byteLength(data)
    },
    rejectUnauthorized: false,
  };

  await makeRequest(options, data);
  console.log(`Successfully tracked event`);
}

async function makeRequest(options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let responseData = '';
      res.on('data', (chunk) => responseData += chunk);
      res.on('end', () => resolve(responseData));
    });

    req.on('error', (error) => {
      console.log(error)
      console.log('Warning: Request failed', error.message);
      resolve();
    });

    req.setTimeout(5000, () => {
      req.destroy();
      resolve();
    });

    req.write(data);
    req.end();
  });
}
try {
  (async () => {
    try {
      const config = readAgentConfig(configPath);

      if (!config || !config.apiKey || !config.target) {
        console.error("Invalid configuration: Missing API key or URL");
        return; // Return instead of throw
      }
      await trackPostInstall(config);
    } catch (error) {
      console.error("Pre-install script failed", error);
    } finally {
      process.exit(0);
    }
  })();
} catch (error) {
  console.error("Fatal error:", error);
  process.exit(0); // Still exit with 0 to not break npm install
}


// Ensure we always exit safely, even if there's an uncaught error
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  process.exit(0);
});

// Ensure we always exit safely, even if there's an unhandled rejection
process.on('unhandledRejection', (error) => {
  console.error('Unhandled Rejection:', error);
  process.exit(0);
});

// Set a timeout to ensure the script doesn't hang
setTimeout(() => {
  console.log('Script timeout reached, exiting safely');
  process.exit(0);
}, 10000);

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

The script reads a configuration file from a sensitive location (/etc/mw-agent/agent-config.yaml) to extract an API key and a target URL, which it then uses to send tracking data to a remote server. This is dangerous because it could expose sensitive information such as the API key and system-related data to an external server, enabling malicious actors to exploit the information. Additionally, the rejectUnauthorized: false setting for HTTPS requests can open the door for man-in-the-middle attacks, compromising data integrity and confidentiality.

Install script:
node scripts/preinstall.js
Install script code:
const path = require("path");
const fs = require("fs");
const os = require("os");
const https = require('https')

const API_URI = 'api/v1/apm/tracking'
const configPath = path.join("/etc", "mw-agent", "agent-config.yaml");

function readAgentConfig(configPath) {
  try {
    // Read the file content

    if (fs.existsSync(configPath)) {
      const fileContent = fs.readFileSync(configPath, "utf8");

      // Split into lines and process
      const lines = fileContent.split("\n");

      let apiKey = "";
      let target = "";

      for (const line of lines) {
        // Skip comments and empty lines
        if (line.trim().startsWith("#") || !line.trim()) {
          continue;
        }

        // Look for api-key
        if (line.includes("api-key:")) {
          apiKey = line.split("api-key:")[1].trim();
        }

        // Look for target
        if (line.includes("target:")) {
          target = line
            .split("target:")[1]
            .trim();
        }
      }

      return {
        apiKey,
        target,
      };
    } else {
      console.error(`Config file not found `);
    }
  } catch (error) {
    console.error("Error reading config file:", error);
    //throw error;
  }

  return null;
}

async function trackPreInstall(config) {

  const payload = {
    status: "apm_tried",
    metadata: {
      host_id: os.hostname(),
      os_type: os.platform(),
      apm_type: "Node",
      apm_data: {
        script: 'npm-install',
        os_version: os.release(),
        node_version: process.version,
        package_name: process.env.npm_package_name,
        package_version: process.env.npm_package_version,
        npm_lifecycle_event: process.env.npm_lifecycle_event,
        reason: 'PreInstall tracking'
      }
    }
  };

  const data = JSON.stringify(payload);
  const baseUrl = new URL(config.target);
  const pathSuffix = `${API_URI}/${config.apiKey}`;
  if (!baseUrl.pathname.endsWith('/')) {
    baseUrl.pathname += '/';
  }
  baseUrl.pathname += pathSuffix;

  const options = {
    method: 'POST',
    hostname: baseUrl.hostname,
    path: baseUrl.pathname,
    port: 443,
    headers: {
      'Content-Type': 'application/json',
    },
    rejectUnauthorized: false,
  };

  await makeRequest(options, data);
  console.log(`Successfully tracked event`);
}

async function makeRequest(options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let responseData = '';
      res.on('data', (chunk) => responseData += chunk);
      res.on('end', () => resolve(responseData));
    });

    req.on('error', (error) => {
      console.log(error)
      console.log('Warning: Request failed', error.message);
      resolve();
    });

    req.setTimeout(5000, () => {
      req.destroy();
      resolve();
    });

    req.write(data);
    req.end();
  });
}
try {
  (async () => {
    try {
      const config = readAgentConfig(configPath);

      if (!config || !config.apiKey || !config.target) {
        console.error("Invalid configuration: Missing API key or URL");
        return; // Return instead of throw
      }
      await trackPreInstall(config);
    } catch (error) {
      console.error("Pre-install script failed", error);
    } finally {
      process.exit(0);
    }
  })();
} catch (error) {
  console.error("Fatal error:", error);
  process.exit(0); // Still exit with 0 to not break npm install
}


// Ensure we always exit safely, even if there's an uncaught error
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  process.exit(0);
});

// Ensure we always exit safely, even if there's an unhandled rejection
process.on('unhandledRejection', (error) => {
  console.error('Unhandled Rejection:', error);
  process.exit(0);
});

// Set a timeout to ensure the script doesn't hang
setTimeout(() => {
  console.log('Script timeout reached, exiting safely');
  process.exit(0);
}, 10000);



Detected: 11 Feb 2025
Detected Date: 11 Feb 2025
Affected Install Script: postinstall
Package Source: ↗️ View on Npm

The script reads sensitive configuration data from a file located at /etc/mw-agent/agent-config.yaml, including an API key that is then sent to a configurable remote server without proper validation or authorization checks. This could facilitate unauthorized tracking or data exfiltration. Additionally, the script disables SSL certificate verification (with rejectUnauthorized: false), making it vulnerable to man-in-the-middle attacks, allowing an attacker to intercept or modify the communication.

Install script:
node scripts/postinstall.js
Install script code:
const path = require("path");
const fs = require("fs");
const os = require("os");
const https = require('https')

const API_URI = 'api/v1/apm/tracking'
const configPath = path.join("/etc", "mw-agent", "agent-config.yaml");

function readAgentConfig(configPath) {
  try {
    // Read the file content

    if (fs.existsSync(configPath)) {
      const fileContent = fs.readFileSync(configPath, "utf8");

      // Split into lines and process
      const lines = fileContent.split("\n");

      let apiKey = "";
      let target = "";

      for (const line of lines) {
        // Skip comments and empty lines
        if (line.trim().startsWith("#") || !line.trim()) {
          continue;
        }

        // Look for api-key
        if (line.includes("api-key:")) {
          apiKey = line.split("api-key:")[1].trim();
        }

        // Look for target
        if (line.includes("target:")) {
          target = line
            .split("target:")[1]
            .trim();
        }
      }

      return {
        apiKey,
        target,
      };
    } else {
      console.error(`Config file not found `);
    }
  } catch (error) {
    console.error("Error reading config file:", error);
  }

  return null;
}

async function trackPostInstall(config) {

  const payload = {
    status: "apm_installed",
    metadata: {
      host_id: os.hostname(),
      os_type: os.platform(),
      apm_type: "Node",
      apm_data: {
        script: 'npm-install',
        os_version: os.release(),
        node_version: process.version,
        package_name: process.env.npm_package_name,
        package_version: process.env.npm_package_version,
        npm_lifecycle_event: process.env.npm_lifecycle_event,
        reason: 'PostInstall tracking'
      }
    }
  };

  const data = JSON.stringify(payload);
  const baseUrl = new URL(config.target);
  const pathSuffix = `${API_URI}/${config.apiKey}`;
  if (!baseUrl.pathname.endsWith('/')) {
    baseUrl.pathname += '/';
  }
  baseUrl.pathname += pathSuffix;

  const options = {
    method: 'POST',
    hostname: baseUrl.hostname,
    path: baseUrl.pathname,
    port: 443,
    headers: {
      'Content-Type': 'application/json',
      //'Content-Length': Buffer.byteLength(data)
    },
    rejectUnauthorized: false,
  };

  await makeRequest(options, data);
  console.log(`Successfully tracked event`);
}

async function makeRequest(options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let responseData = '';
      res.on('data', (chunk) => responseData += chunk);
      res.on('end', () => resolve(responseData));
    });

    req.on('error', (error) => {
      console.log(error)
      console.log('Warning: Request failed', error.message);
      resolve();
    });

    req.setTimeout(5000, () => {
      req.destroy();
      resolve();
    });

    req.write(data);
    req.end();
  });
}
try {
  (async () => {
    try {
      const config = readAgentConfig(configPath);

      if (!config || !config.apiKey || !config.target) {
        console.error("Invalid configuration: Missing API key or URL");
        return; // Return instead of throw
      }
      await trackPostInstall(config);
    } catch (error) {
      console.error("Pre-install script failed", error);
    } finally {
      process.exit(0);
    }
  })();
} catch (error) {
  console.error("Fatal error:", error);
  process.exit(0); // Still exit with 0 to not break npm install
}


// Ensure we always exit safely, even if there's an uncaught error
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  process.exit(0);
});

// Ensure we always exit safely, even if there's an unhandled rejection
process.on('unhandledRejection', (error) => {
  console.error('Unhandled Rejection:', error);
  process.exit(0);
});

// Set a timeout to ensure the script doesn't hang
setTimeout(() => {
  console.log('Script timeout reached, exiting safely');
  process.exit(0);
}, 10000);

Detected: 11 Feb 2025
Detected Date: 11 Feb 2025
Affected Install Script: preinstall
Package Source: ↗️ View on Npm

The script reads a configuration file for sensitive information like an API key and a target URL. It then makes an HTTPS POST request to a potentially external server with data that includes the API key and system information. There are risks associated with sending sensitive data over the network, especially since the URL can be configured dynamically and the script ignores SSL certificate verification (rejectUnauthorized: false), making it susceptible to man-in-the-middle attacks.

Install script:
node scripts/preinstall.js
Install script code:
const path = require("path");
const fs = require("fs");
const os = require("os");
const https = require('https')

const API_URI = 'api/v1/apm/tracking'
const configPath = path.join("/etc", "mw-agent", "agent-config.yaml");

function readAgentConfig(configPath) {
  try {
    // Read the file content

    if (fs.existsSync(configPath)) {
      const fileContent = fs.readFileSync(configPath, "utf8");

      // Split into lines and process
      const lines = fileContent.split("\n");

      let apiKey = "";
      let target = "";

      for (const line of lines) {
        // Skip comments and empty lines
        if (line.trim().startsWith("#") || !line.trim()) {
          continue;
        }

        // Look for api-key
        if (line.includes("api-key:")) {
          apiKey = line.split("api-key:")[1].trim();
        }

        // Look for target
        if (line.includes("target:")) {
          target = line
            .split("target:")[1]
            .trim();
        }
      }

      return {
        apiKey,
        target,
      };
    } else {
      console.error(`Config file not found `);
    }
  } catch (error) {
    console.error("Error reading config file:", error);
    //throw error;
  }

  return null;
}

async function trackPreInstall(config) {

  const payload = {
    status: "apm_tried",
    metadata: {
      host_id: os.hostname(),
      os_type: os.platform(),
      apm_type: "Node",
      apm_data: {
        script: 'npm-install',
        os_version: os.release(),
        node_version: process.version,
        package_name: process.env.npm_package_name,
        package_version: process.env.npm_package_version,
        npm_lifecycle_event: process.env.npm_lifecycle_event,
        reason: 'PreInstall tracking'
      }
    }
  };

  const data = JSON.stringify(payload);
  const baseUrl = new URL(config.target);
  const pathSuffix = `${API_URI}/${config.apiKey}`;
  if (!baseUrl.pathname.endsWith('/')) {
    baseUrl.pathname += '/';
  }
  baseUrl.pathname += pathSuffix;

  const options = {
    method: 'POST',
    hostname: baseUrl.hostname,
    path: baseUrl.pathname,
    port: 443,
    headers: {
      'Content-Type': 'application/json',
    },
    rejectUnauthorized: false,
  };

  await makeRequest(options, data);
  console.log(`Successfully tracked event`);
}

async function makeRequest(options, data) {
  return new Promise((resolve, reject) => {
    const req = https.request(options, (res) => {
      let responseData = '';
      res.on('data', (chunk) => responseData += chunk);
      res.on('end', () => resolve(responseData));
    });

    req.on('error', (error) => {
      console.log(error)
      console.log('Warning: Request failed', error.message);
      resolve();
    });

    req.setTimeout(5000, () => {
      req.destroy();
      resolve();
    });

    req.write(data);
    req.end();
  });
}
try {
  (async () => {
    try {
      const config = readAgentConfig(configPath);

      if (!config || !config.apiKey || !config.target) {
        console.error("Invalid configuration: Missing API key or URL");
        return; // Return instead of throw
      }
      await trackPreInstall(config);
    } catch (error) {
      console.error("Pre-install script failed", error);
    } finally {
      process.exit(0);
    }
  })();
} catch (error) {
  console.error("Fatal error:", error);
  process.exit(0); // Still exit with 0 to not break npm install
}


// Ensure we always exit safely, even if there's an uncaught error
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  process.exit(0);
});

// Ensure we always exit safely, even if there's an unhandled rejection
process.on('unhandledRejection', (error) => {
  console.error('Unhandled Rejection:', error);
  process.exit(0);
});

// Set a timeout to ensure the script doesn't hang
setTimeout(() => {
  console.log('Script timeout reached, exiting safely');
  process.exit(0);
}, 10000);