added arborist.js file & support for npm auth token for arborist lib to scan private deps

This commit is contained in:
Sahilb315
2025-04-10 01:10:56 +05:30
parent ed8f9b6d53
commit 2d2f0fdee9
6 changed files with 101 additions and 13 deletions
+6 -2
View File
@@ -49,7 +49,7 @@ func NewNpmCommand() *cobra.Command {
return fmt.Errorf("npm not found: %w", err)
}
return utils.ExecCmd(npmPath, args)
return utils.ExecCmd(npmPath, args, []string{})
},
}
return cmd
@@ -71,7 +71,11 @@ func wrapNpm() error {
Interpreter: "node",
ScriptType: "js",
Args: []string{},
Env: map[string]string{
"NPM_AUTH_TOKEN": utils.NpmAuthToken(),
},
})
if err != nil {
return fmt.Errorf("failed to extract package info: %w", err)
}
@@ -154,7 +158,7 @@ func wrapNpm() error {
// Install the package and return
cmdArgs := []string{action, packageName}
if err = utils.ExecCmd(npmPath, cmdArgs); err != nil {
if err = utils.ExecCmd(npmPath, cmdArgs, []string{}); err != nil {
return fmt.Errorf("failed to execute npm command: %w", err)
}
+10 -4
View File
@@ -69306,11 +69306,11 @@ var require_lib43 = __commonJS({
// arborist.js
var Arborist = require_lib43();
var fs = require("fs");
async function getDependencyTree(packageName) {
async function getDependencyTree(packageName, authToken2) {
const arb = new Arborist({
registry: "https://registry.npmjs.org",
token: "",
authToken: ""
token: authToken2,
authToken: authToken2
});
try {
const idealTree = await arb.buildIdealTree({
@@ -69337,6 +69337,7 @@ function writeToFile(packages, filename) {
}
var packageArg = process.argv[2];
var outputFile = process.argv[3];
var authToken = process.env.NPM_AUTH_TOKEN;
if (!packageArg) {
console.error("Please provide a package name as an argument");
process.exit(1);
@@ -69345,7 +69346,12 @@ if (!outputFile) {
console.error("Please provide an output filename as the second argument");
process.exit(1);
}
getDependencyTree(packageArg).then((packages) => {
if (!authToken) {
console.warn(
"NPM token not found. Some private or scoped dependencies may not be included in the scan."
);
}
getDependencyTree(packageArg, authToken).then((packages) => {
writeToFile(packages, outputFile);
}).catch((err) => {
console.error("Error:", err);
+67
View File
@@ -0,0 +1,67 @@
const Arborist = require("@npmcli/arborist");
const fs = require("fs");
async function getDependencyTree(packageName, authToken) {
const arb = new Arborist({
registry: "https://registry.npmjs.org",
token: authToken,
authToken: authToken,
});
try {
const idealTree = await arb.buildIdealTree({
add: [packageName],
});
const packageNames = [];
idealTree.children.forEach((node) => {
packageNames.push(`${node.name}@${node.version}`);
});
return packageNames;
} catch (error) {
console.error(`Failed to fetch dependency tree for ${packageName}:`, error);
return [];
}
}
/**
* Write dependency list to a file
* @param {string[]} packages - List of package dependencies
* @param {string} filename - Output filename
*/
function writeToFile(packages, filename) {
try {
fs.writeFileSync(filename, packages.join("\n"), "utf8");
console.log(`Dependencies written to ${filename}`);
} catch (error) {
console.error(`Failed to write to file ${filename}:`, error);
process.exit(1);
}
}
const packageArg = process.argv[2];
const outputFile = process.argv[3];
const authToken = process.env.NPM_AUTH_TOKEN;
if (!packageArg) {
console.error("Please provide a package name as an argument");
process.exit(1);
}
if (!outputFile) {
console.error("Please provide an output filename as the second argument");
process.exit(1);
}
if (!authToken) {
console.warn(
"NPM token not found. Some private or scoped dependencies may not be included in the scan.",
);
}
getDependencyTree(packageArg, authToken)
.then((packages) => {
writeToFile(packages, outputFile);
})
.catch((err) => {
console.error("Error:", err);
process.exit(1);
});
+11 -6
View File
@@ -11,11 +11,12 @@ import (
// ExtractorOptions holds configuration for running an extractor script
type ExtractorOptions struct {
ScriptContent string // The script content
ScriptType string // File extension like "js", "py", etc.
Interpreter string // What interpreter to use (e.g., "node", "python")
PackageName string // Name of the package to analyze
Args []string // Additional arguments to pass to the script
ScriptContent string // The script content
ScriptType string // File extension like "js", "py", etc.
Interpreter string // What interpreter to use (e.g., "node", "python")
PackageName string // Name of the package to analyze
Args []string // Additional arguments to pass to the script
Env map[string]string // Environment variables to pass to the script
}
// RunExtractor extracts an embedded script to a temp file and executes it
@@ -50,7 +51,11 @@ func RunPkgExtractor(opts ExtractorOptions) (string, error) {
// Build the command with all arguments
cmdArgs := append([]string{scriptFile.Name(), opts.PackageName, outputFile}, opts.Args...)
if err = utils.ExecCmd(interpreterPath, cmdArgs); err != nil {
var env []string
for key, value := range opts.Env {
env = append(env, fmt.Sprintf("%s=%s", key, value))
}
if err = utils.ExecCmd(interpreterPath, cmdArgs, env); err != nil {
return "", err
}
+3 -1
View File
@@ -3,11 +3,13 @@ package utils
import (
"bytes"
"fmt"
"os"
"os/exec"
)
func ExecCmd(name string, args []string) error {
func ExecCmd(name string, args, env []string) error {
cmd := exec.Command(name, args...)
cmd.Env = append(os.Environ(), env...)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
+4
View File
@@ -9,3 +9,7 @@ func ApiKey() string {
func TenantDomain() string {
return os.Getenv("SAFEDEP_TENANT_ID")
}
func NpmAuthToken() string {
return os.Getenv("NPM_AUTH_TOKEN")
}