๐ŸŽฉ You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP โ†’
Sign In

dep-inspector-cli

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dep-inspector-cli - npm Package Compare versions

Comparing version
1.1.0
to
2.0.0
+6
dist/commands/scan-all.d.ts
export declare function scanAll(options: {
ai?: boolean;
json?: boolean;
report?: boolean;
}): Promise<void>;
//# sourceMappingURL=scan-all.d.ts.map
{"version":3,"file":"scan-all.d.ts","sourceRoot":"","sources":["../../src/commands/scan-all.ts"],"names":[],"mappings":"AAQA,wBAAsB,OAAO,CAAC,OAAO,EAAE;IAAE,EAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,iBAexF"}
import { scanSecrets } from "./scan-secrets.js";
import { scanDocker } from "./scan-docker.js";
import { scanCI } from "./scan-ci.js";
import { scanPorts } from "./scan-ports.js";
import { scanLogs } from "./scan-logs.js";
import { analyzeProject } from "./analyze.js";
import chalk from "chalk";
export async function scanAll(options) {
console.log(chalk.bold.magenta("\n๐Ÿš€ dep-inspector v2 โ€” Full DevOps Scan\n"));
console.log(chalk.gray("โ”€".repeat(50)));
const jsonOpt = options.json ? { json: true } : {};
const aiOpt = options.ai ? { ai: true } : {};
await analyzeProject({ ai: false, ...jsonOpt, depth: 2 });
await scanSecrets({ dir: ".", ...jsonOpt, ...aiOpt });
await scanDocker({ file: "Dockerfile", ...jsonOpt });
await scanCI({ dir: ".github/workflows", ...jsonOpt });
await scanPorts({ ...jsonOpt });
await scanLogs({ ...jsonOpt });
console.log(chalk.bold.magenta("\nโœ… Full scan complete.\n"));
}
//# sourceMappingURL=scan-all.js.map
{"version":3,"file":"scan-all.js","sourceRoot":"","sources":["../../src/commands/scan-all.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAO,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAW,cAAc,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAQ,iBAAiB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAS,gBAAgB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAA2D;IACvF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC,CAAC;IAC9E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAExC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,MAAM,KAAK,GAAK,OAAO,CAAC,EAAE,CAAG,CAAC,CAAC,EAAE,EAAE,EAAE,IAAa,EAAE,CAAG,CAAC,CAAC,EAAE,CAAC;IAE5D,MAAM,cAAc,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IAC1D,MAAM,WAAW,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IACtD,MAAM,UAAU,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACrD,MAAM,MAAM,CAAC,EAAE,GAAG,EAAE,mBAAmB,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACvD,MAAM,SAAS,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IAChC,MAAM,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IAE/B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC,CAAC;AAC/D,CAAC"}
export declare function scanCI(options: {
dir: string;
json?: boolean;
}): Promise<void>;
//# sourceMappingURL=scan-ci.d.ts.map
{"version":3,"file":"scan-ci.d.ts","sourceRoot":"","sources":["../../src/commands/scan-ci.ts"],"names":[],"mappings":"AA8CA,wBAAsB,MAAM,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,iBAkCpE"}
import fs from "fs";
import path from "path";
import chalk from "chalk";
function analyzeWorkflow(filePath, content) {
const issues = [];
// 1. Hardcoded secrets in YAML
if (/password:|secret:|api_key:/i.test(content) && !content.includes("${{")) {
issues.push({ file: filePath, severity: "HIGH", message: "Possible hardcoded secret in workflow (use ${{ secrets.X }})" });
}
// 2. Using deprecated set-output command
if (content.includes("::set-output")) {
issues.push({ file: filePath, severity: "HIGH", message: "Deprecated '::set-output' command used โ€” replace with $GITHUB_OUTPUT" });
}
// 3. pull_request_target with checkout โ€” dangerous
if (content.includes("pull_request_target") && content.includes("actions/checkout")) {
issues.push({ file: filePath, severity: "HIGH", message: "pull_request_target + actions/checkout is a security risk (privilege escalation)" });
}
// 4. Pinned action versions
if (/uses:\s+\S+@(main|master|latest)/i.test(content)) {
issues.push({ file: filePath, severity: "MEDIUM", message: "Actions using @main/@master/@latest โ€” pin to a specific commit SHA or version" });
}
// 5. No cache step
if (!content.includes("actions/cache") && !content.includes("cache: 'npm'")) {
issues.push({ file: filePath, severity: "LOW", message: "No caching configured โ€” builds will be slow (add actions/cache for node_modules)" });
}
// 6. No timeout-minutes
if (!content.includes("timeout-minutes")) {
issues.push({ file: filePath, severity: "LOW", message: "No timeout-minutes set โ€” stuck jobs can run indefinitely" });
}
return issues;
}
export async function scanCI(options) {
const workflowDir = options.dir;
const allIssues = [];
if (!fs.existsSync(workflowDir)) {
console.log(chalk.yellow(`โš ๏ธ No workflows directory found at ${workflowDir}`));
return;
}
const files = fs.readdirSync(workflowDir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
for (const file of files) {
const fullPath = path.join(workflowDir, file);
const content = fs.readFileSync(fullPath, "utf-8");
allIssues.push(...analyzeWorkflow(fullPath, content));
}
if (options.json) {
console.log(JSON.stringify({ issues: allIssues, total: allIssues.length }, null, 2));
return;
}
console.log(chalk.bold.cyan("\nโš™๏ธ CI/CD Pipeline Analysis\n"));
if (allIssues.length === 0) {
console.log(chalk.green("โœ… All workflows look good!"));
return;
}
for (const issue of allIssues) {
const color = issue.severity === "HIGH" ? chalk.red : issue.severity === "MEDIUM" ? chalk.yellow : chalk.gray;
console.log(color(`[${issue.severity}] ${path.basename(issue.file)}: ${issue.message}`));
}
console.log();
}
//# sourceMappingURL=scan-ci.js.map
{"version":3,"file":"scan-ci.js","sourceRoot":"","sources":["../../src/commands/scan-ci.ts"],"names":[],"mappings":"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"}
export declare function scanDocker(options: {
file: string;
compose?: string;
json?: boolean;
}): Promise<void>;
//# sourceMappingURL=scan-docker.d.ts.map
{"version":3,"file":"scan-docker.d.ts","sourceRoot":"","sources":["../../src/commands/scan-docker.ts"],"names":[],"mappings":"AAmDA,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,iBA2B3F"}
import fs from "fs";
import chalk from "chalk";
function analyzeDockerfile(content) {
const issues = [];
const lines = content.split("\n");
// 1. Running as root
const hasUser = lines.some((l) => l.trim().startsWith("USER ") && !l.includes("root"));
if (!hasUser)
issues.push({ severity: "HIGH", message: "No non-root USER defined โ€” container runs as root" });
// 2. HEALTHCHECK missing
if (!content.includes("HEALTHCHECK")) {
issues.push({ severity: "MEDIUM", message: "No HEALTHCHECK instruction โ€” Docker can't auto-restart unhealthy containers" });
}
// 3. latest tag usage
if (/FROM\s+\S+:latest/i.test(content)) {
issues.push({ severity: "MEDIUM", message: "Using ':latest' tag โ€” not reproducible, pin a specific version" });
}
// 4. Secrets in ENV/ARG
const secretPattern = /(?:ENV|ARG)\s+(?:PASSWORD|SECRET|KEY|TOKEN|API_KEY)\s*=/i;
if (secretPattern.test(content)) {
issues.push({ severity: "HIGH", message: "Sensitive variable (PASSWORD/SECRET/KEY) hardcoded in ENV/ARG" });
}
// 5. No .dockerignore warning (check file exists)
if (!fs.existsSync(".dockerignore")) {
issues.push({ severity: "LOW", message: ".dockerignore missing โ€” may include node_modules or .env in image" });
}
// 6. npm install without --omit=dev in production
if (content.includes("npm install") && !content.includes("--omit=dev") && !content.includes("--only=production")) {
issues.push({ severity: "LOW", message: "'npm install' without --omit=dev โ€” devDependencies included in image" });
}
// 7. Multi-stage โ€” good practice check
const fromCount = (content.match(/^FROM\s/gim) ?? []).length;
if (fromCount === 1) {
issues.push({ severity: "LOW", message: "Single-stage build โ€” consider multi-stage to reduce image size" });
}
return issues;
}
export async function scanDocker(options) {
const issues = [];
if (fs.existsSync(options.file)) {
const content = fs.readFileSync(options.file, "utf-8");
issues.push(...analyzeDockerfile(content));
}
else {
console.log(chalk.yellow(`โš ๏ธ Dockerfile not found at ${options.file}`));
}
if (options.json) {
console.log(JSON.stringify({ issues, total: issues.length }, null, 2));
return;
}
console.log(chalk.bold.cyan("\n๐Ÿณ Docker Analysis\n"));
if (issues.length === 0) {
console.log(chalk.green("โœ… Dockerfile looks good!"));
return;
}
for (const issue of issues) {
const color = issue.severity === "HIGH" ? chalk.red : issue.severity === "MEDIUM" ? chalk.yellow : chalk.gray;
console.log(color(`[${issue.severity}] ${issue.message}`));
}
console.log();
}
//# sourceMappingURL=scan-docker.js.map
{"version":3,"file":"scan-docker.js","sourceRoot":"","sources":["../../src/commands/scan-docker.ts"],"names":[],"mappings":"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"}
export declare function scanLogs(options: {
json?: boolean;
}): Promise<void>;
//# sourceMappingURL=scan-logs.d.ts.map
{"version":3,"file":"scan-logs.d.ts","sourceRoot":"","sources":["../../src/commands/scan-logs.ts"],"names":[],"mappings":"AAGA,wBAAsB,QAAQ,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,iBAsDzD"}
import fs from "fs";
import chalk from "chalk";
export async function scanLogs(options) {
const issues = [];
const passed = [];
// Check if Winston or Morgan installed
const pkgPath = "package.json";
if (!fs.existsSync(pkgPath)) {
console.log(chalk.yellow("โš ๏ธ package.json not found"));
return;
}
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
const hasWinston = "winston" in allDeps;
const hasMorgan = "morgan" in allDeps;
const hasPino = "pino" in allDeps;
if (!hasWinston && !hasMorgan && !hasPino) {
issues.push("No logger found (winston/morgan/pino) โ€” console.log is not production-grade");
}
else {
if (hasWinston)
passed.push("winston detected");
if (hasMorgan)
passed.push("morgan detected");
if (hasPino)
passed.push("pino detected");
}
// Check for winston transports (file rotation)
if (hasWinston) {
const hasRotation = "winston-daily-rotate-file" in allDeps;
if (!hasRotation) {
issues.push("winston-daily-rotate-file not found โ€” logs may grow unbounded");
}
else {
passed.push("log rotation configured");
}
}
// Check for LOG_LEVEL env usage
const envFile = fs.existsSync(".env") ? fs.readFileSync(".env", "utf-8") : "";
if (!envFile.includes("LOG_LEVEL")) {
issues.push("LOG_LEVEL not set in .env โ€” logger may default to verbose in production");
}
if (options.json) {
console.log(JSON.stringify({ issues, passed }, null, 2));
return;
}
console.log(chalk.bold.cyan("\n๐Ÿ“‹ Logger Health Check\n"));
for (const p of passed)
console.log(chalk.green(`โœ… ${p}`));
for (const i of issues)
console.log(chalk.yellow(`โš ๏ธ ${i}`));
console.log();
}
//# sourceMappingURL=scan-logs.js.map
{"version":3,"file":"scan-logs.js","sourceRoot":"","sources":["../../src/commands/scan-logs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAA2B;IACxD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,uCAAuC;IACvC,MAAM,OAAO,GAAG,cAAc,CAAC;IAC/B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC;QACxD,OAAO;IACT,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAGvD,CAAC;IACF,MAAM,OAAO,GAAG,EAAE,GAAG,GAAG,CAAC,YAAY,EAAE,GAAG,GAAG,CAAC,eAAe,EAAE,CAAC;IAEhE,MAAM,UAAU,GAAG,SAAS,IAAI,OAAO,CAAC;IACxC,MAAM,SAAS,GAAI,QAAQ,IAAI,OAAO,CAAC;IACvC,MAAM,OAAO,GAAM,MAAM,IAAI,OAAO,CAAC;IAErC,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,EAAE,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;IAC7F,CAAC;SAAM,CAAC;QACN,IAAI,UAAU;YAAE,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAChD,IAAI,SAAS;YAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC/C,IAAI,OAAO;YAAK,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC/C,CAAC;IAED,+CAA+C;IAC/C,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,WAAW,GAAG,2BAA2B,IAAI,OAAO,CAAC;QAC3D,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;QAC/E,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED,gCAAgC;IAChC,MAAM,OAAO,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAC;IACzF,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACzD,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,CAAC;IAC3D,KAAK,MAAM,CAAC,IAAI,MAAM;QAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5D,KAAK,MAAM,CAAC,IAAI,MAAM;QAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/D,OAAO,CAAC,GAAG,EAAE,CAAC;AAChB,CAAC"}
export declare function scanPorts(options: {
json?: boolean;
}): Promise<void>;
//# sourceMappingURL=scan-ports.d.ts.map
{"version":3,"file":"scan-ports.d.ts","sourceRoot":"","sources":["../../src/commands/scan-ports.ts"],"names":[],"mappings":"AAMA,wBAAsB,SAAS,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,iBAkD1D"}
import { execSync } from "child_process";
import chalk from "chalk";
const KNOWN_SAFE_PORTS = [22, 80, 443, 3000, 3001, 4000, 5000, 8080, 8443];
const SUSPICIOUS_PORTS = [21, 23, 25, 445, 1433, 3306, 5432, 6379, 27017]; // ftp, telnet, smtp, smb, mssql, mysql, pg, redis, mongo
export async function scanPorts(options) {
let output = "";
try {
const isWindows = process.platform === "win32";
output = isWindows
? execSync("netstat -ano", { encoding: "utf-8" })
: execSync("ss -tulnp 2>/dev/null || netstat -tulnp 2>/dev/null", {
encoding: "utf-8",
});
}
catch {
console.log(chalk.yellow("โš ๏ธ Could not read port info"));
return;
}
const lines = output
.split("\n")
.filter((l) => l.includes("LISTEN") || l.includes("0.0.0.0"));
const openPorts = [];
for (const line of lines) {
const portMatch = line.match(/:(\d+)\s/);
if (!portMatch)
continue;
const port = parseInt(portMatch[1] ?? "0");
const isExposed = line.includes("0.0.0.0") || line.includes("*:");
const isSuspicious = SUSPICIOUS_PORTS.includes(port) && isExposed;
const entry = {
port,
process: line.split(/\s+/).pop() ?? "unknown",
};
if (isSuspicious)
entry.warning = `Port ${port} is publicly exposed โ€” restrict to localhost`;
openPorts.push(entry);
}
if (options.json) {
console.log(JSON.stringify({ openPorts }, null, 2));
return;
}
console.log(chalk.bold.cyan("\n๐Ÿ”Œ Port & Process Monitor\n"));
for (const p of openPorts) {
if (p.warning) {
console.log(chalk.red(`[WARN] :${p.port} โ€” ${p.warning}`));
}
else {
console.log(chalk.green(`[OK] :${p.port}`));
}
}
console.log();
}
//# sourceMappingURL=scan-ports.js.map
{"version":3,"file":"scan-ports.js","sourceRoot":"","sources":["../../src/commands/scan-ports.ts"],"names":[],"mappings":"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"}
export declare function scanSecrets(options: {
dir: string;
json?: boolean;
ai?: boolean;
}): Promise<void>;
//# sourceMappingURL=scan-secrets.d.ts.map
{"version":3,"file":"scan-secrets.d.ts","sourceRoot":"","sources":["../../src/commands/scan-secrets.ts"],"names":[],"mappings":"AA4FA,wBAAsB,WAAW,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,OAAO,CAAA;CAAE,iBAgDvF"}
import fs from "fs";
import path from "path";
import chalk from "chalk";
// Secret patterns โ€” no AI needed
const SECRET_PATTERNS = [
{ name: "AWS Access Key", pattern: /AKIA[0-9A-Z]{16}/, severity: "HIGH" },
{ name: "AWS Secret Key", pattern: /aws_secret_access_key\s*=\s*[^\s]{20,}/i, severity: "HIGH" },
{ name: "Private Key Block", pattern: /-----BEGIN (RSA |EC )?PRIVATE KEY-----/, severity: "HIGH" },
{ name: "Groq API Key", pattern: /gsk_[a-zA-Z0-9]{40,}/, severity: "HIGH" },
{ name: "OpenAI API Key", pattern: /sk-[a-zA-Z0-9]{32,}/, severity: "HIGH" },
{ name: "GitHub Token", pattern: /ghp_[a-zA-Z0-9]{36}/, severity: "HIGH" },
{ name: "JWT Secret hardcoded", pattern: /jwt[_-]?secret\s*[:=]\s*['"][^'"]{8,}/i, severity: "HIGH" },
{ name: "Database URL", pattern: /mongodb(\+srv)?:\/\/[^:]+:[^@]+@/i, severity: "HIGH" },
{ name: "Database URL (PG)", pattern: /postgres(ql)?:\/\/[^:]+:[^@]+@/i, severity: "HIGH" },
{ name: "Stripe Secret Key", pattern: /sk_live_[a-zA-Z0-9]{24,}/, severity: "HIGH" },
{ name: "Razorpay Secret", pattern: /rzp_live_[a-zA-Z0-9]{14,}/, severity: "HIGH" },
{ name: "Generic Password", pattern: /password\s*[:=]\s*['"][^'"]{6,}/i, severity: "MEDIUM" },
{ name: "Generic Secret", pattern: /secret\s*[:=]\s*['"][^'"]{6,}/i, severity: "MEDIUM" },
{ name: ".env file committed", pattern: /^\.env$/, severity: "HIGH" }, // filename check
];
const IGNORE_DIRS = ["node_modules", ".git", "dist", ".next", "build", "coverage"];
const IGNORE_FILES = [".env", ".env.local", ".env.development", ".env.production"];
const SCAN_EXTENSIONS = [".ts", ".js", ".tsx", ".jsx", ".env", ".json", ".yaml", ".yml", ".sh"];
function shouldIgnore(filePath) {
const fileName = path.basename(filePath);
if (IGNORE_FILES.includes(fileName))
return true; // ๐Ÿ‘ˆ add this
return IGNORE_DIRS.some((dir) => filePath.includes(`/${dir}/`));
}
function checkEnvInGitignore() {
if (!fs.existsSync(".gitignore")) {
console.log(chalk.red("[HIGH] .gitignore missing โ€” .env may get committed to git"));
return;
}
const gitignore = fs.readFileSync(".gitignore", "utf-8");
if (!gitignore.includes(".env")) {
console.log(chalk.red("[HIGH] .env is NOT in .gitignore โ€” risk of secret exposure in git"));
}
else {
console.log(chalk.green("โœ… .env is gitignored"));
}
}
function scanFile(filePath) {
const findings = [];
const ext = path.extname(filePath);
if (!SCAN_EXTENSIONS.includes(ext) && !filePath.endsWith(".env"))
return findings;
if (shouldIgnore(filePath))
return findings;
const lines = fs.readFileSync(filePath, "utf-8").split("\n");
lines.forEach((line, idx) => {
for (const { name, pattern, severity } of SECRET_PATTERNS) {
if (pattern.test(line)) {
findings.push({
file: filePath,
line: idx + 1,
pattern: name,
severity,
preview: line.trim().slice(0, 80),
});
break;
}
}
});
return findings;
}
function walkDir(dir) {
const files = [];
if (!fs.existsSync(dir))
return files;
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory() && !IGNORE_DIRS.includes(entry.name)) {
files.push(...walkDir(fullPath));
}
else if (entry.isFile()) {
files.push(fullPath);
}
}
return files;
}
export async function scanSecrets(options) {
const targetDir = path.resolve(options.dir);
const allFiles = walkDir(targetDir);
const allFindings = [];
for (const file of allFiles) {
allFindings.push(...scanFile(file));
}
if (options.json) {
console.log(JSON.stringify({ findings: allFindings, total: allFindings.length }, null, 2));
return;
}
console.log(chalk.bold.cyan("\n๐Ÿ” Secrets Scanner\n"));
checkEnvInGitignore();
if (allFindings.length === 0) {
console.log(chalk.green("โœ… No secrets detected!"));
return;
}
const highCount = allFindings.filter((f) => f.severity === "HIGH").length;
const medCount = allFindings.filter((f) => f.severity === "MEDIUM").length;
console.log(chalk.red(`โŒ ${highCount} HIGH`) + " " + chalk.yellow(`โš ๏ธ ${medCount} MEDIUM`) + "\n");
for (const f of allFindings) {
const color = f.severity === "HIGH" ? chalk.red : chalk.yellow;
console.log(color(`[${f.severity}] ${f.pattern}`));
console.log(` File : ${f.file}:${f.line}`);
console.log(` Code : ${chalk.gray(f.preview)}\n`);
}
if (options.ai) {
const key = process.env["GROQ_API_KEY"];
if (!key) {
console.log(chalk.gray("โ„น๏ธ AI insights skipped โ€” GROQ_API_KEY not set"));
}
else {
// dynamic import to avoid breaking non-AI usage
const { analyzeWithAI } = await import("../utils/ai.js");
const summary = await analyzeWithAI(allFindings.map((f) => `${f.pattern} in ${f.file}:${f.line}`).join("\n"));
console.log(chalk.cyan("\n๐Ÿ’ก AI Insights\n") + summary);
}
}
}
//# sourceMappingURL=scan-secrets.js.map
{"version":3,"file":"scan-secrets.js","sourceRoot":"","sources":["../../src/commands/scan-secrets.ts"],"names":[],"mappings":"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"}
export {};
//# sourceMappingURL=config.d.ts.map
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":""}
export {};
//# sourceMappingURL=config.js.map
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":""}
export {};
//# sourceMappingURL=reporter.d.ts.map
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/utils/reporter.ts"],"names":[],"mappings":""}
export {};
//# sourceMappingURL=reporter.js.map
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/utils/reporter.ts"],"names":[],"mappings":""}
import { scanSecrets } from "./scan-secrets.js";
import { scanDocker } from "./scan-docker.js";
import { scanCI } from "./scan-ci.js";
import { scanPorts } from "./scan-ports.js";
import { scanLogs } from "./scan-logs.js";
import { analyzeProject } from "./analyze.js";
import chalk from "chalk";
export async function scanAll(options: { ai?: boolean; json?: boolean; report?: boolean }) {
console.log(chalk.bold.magenta("\n๐Ÿš€ dep-inspector v2 โ€” Full DevOps Scan\n"));
console.log(chalk.gray("โ”€".repeat(50)));
const jsonOpt = options.json ? { json: true as const } : {};
const aiOpt = options.ai ? { ai: true as const } : {};
await analyzeProject({ ai: false, ...jsonOpt, depth: 2 });
await scanSecrets({ dir: ".", ...jsonOpt, ...aiOpt });
await scanDocker({ file: "Dockerfile", ...jsonOpt });
await scanCI({ dir: ".github/workflows", ...jsonOpt });
await scanPorts({ ...jsonOpt });
await scanLogs({ ...jsonOpt });
console.log(chalk.bold.magenta("\nโœ… Full scan complete.\n"));
}
import fs from "fs";
import path from "path";
import chalk from "chalk";
interface CIIssue {
file: string;
severity: "HIGH" | "MEDIUM" | "LOW";
message: string;
}
function analyzeWorkflow(filePath: string, content: string): CIIssue[] {
const issues: CIIssue[] = [];
// 1. Hardcoded secrets in YAML
if (/password:|secret:|api_key:/i.test(content) && !content.includes("${{")) {
issues.push({ file: filePath, severity: "HIGH", message: "Possible hardcoded secret in workflow (use ${{ secrets.X }})" });
}
// 2. Using deprecated set-output command
if (content.includes("::set-output")) {
issues.push({ file: filePath, severity: "HIGH", message: "Deprecated '::set-output' command used โ€” replace with $GITHUB_OUTPUT" });
}
// 3. pull_request_target with checkout โ€” dangerous
if (content.includes("pull_request_target") && content.includes("actions/checkout")) {
issues.push({ file: filePath, severity: "HIGH", message: "pull_request_target + actions/checkout is a security risk (privilege escalation)" });
}
// 4. Pinned action versions
if (/uses:\s+\S+@(main|master|latest)/i.test(content)) {
issues.push({ file: filePath, severity: "MEDIUM", message: "Actions using @main/@master/@latest โ€” pin to a specific commit SHA or version" });
}
// 5. No cache step
if (!content.includes("actions/cache") && !content.includes("cache: 'npm'")) {
issues.push({ file: filePath, severity: "LOW", message: "No caching configured โ€” builds will be slow (add actions/cache for node_modules)" });
}
// 6. No timeout-minutes
if (!content.includes("timeout-minutes")) {
issues.push({ file: filePath, severity: "LOW", message: "No timeout-minutes set โ€” stuck jobs can run indefinitely" });
}
return issues;
}
export async function scanCI(options: { dir: string; json?: boolean }) {
const workflowDir = options.dir;
const allIssues: CIIssue[] = [];
if (!fs.existsSync(workflowDir)) {
console.log(chalk.yellow(`โš ๏ธ No workflows directory found at ${workflowDir}`));
return;
}
const files = fs.readdirSync(workflowDir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
for (const file of files) {
const fullPath = path.join(workflowDir, file);
const content = fs.readFileSync(fullPath, "utf-8");
allIssues.push(...analyzeWorkflow(fullPath, content));
}
if (options.json) {
console.log(JSON.stringify({ issues: allIssues, total: allIssues.length }, null, 2));
return;
}
console.log(chalk.bold.cyan("\nโš™๏ธ CI/CD Pipeline Analysis\n"));
if (allIssues.length === 0) {
console.log(chalk.green("โœ… All workflows look good!"));
return;
}
for (const issue of allIssues) {
const color = issue.severity === "HIGH" ? chalk.red : issue.severity === "MEDIUM" ? chalk.yellow : chalk.gray;
console.log(color(`[${issue.severity}] ${path.basename(issue.file)}: ${issue.message}`));
}
console.log();
}
import fs from "fs";
import chalk from "chalk";
interface DockerIssue {
severity: "HIGH" | "MEDIUM" | "LOW";
message: string;
}
function analyzeDockerfile(content: string): DockerIssue[] {
const issues: DockerIssue[] = [];
const lines = content.split("\n");
// 1. Running as root
const hasUser = lines.some((l) => l.trim().startsWith("USER ") && !l.includes("root"));
if (!hasUser) issues.push({ severity: "HIGH", message: "No non-root USER defined โ€” container runs as root" });
// 2. HEALTHCHECK missing
if (!content.includes("HEALTHCHECK")) {
issues.push({ severity: "MEDIUM", message: "No HEALTHCHECK instruction โ€” Docker can't auto-restart unhealthy containers" });
}
// 3. latest tag usage
if (/FROM\s+\S+:latest/i.test(content)) {
issues.push({ severity: "MEDIUM", message: "Using ':latest' tag โ€” not reproducible, pin a specific version" });
}
// 4. Secrets in ENV/ARG
const secretPattern = /(?:ENV|ARG)\s+(?:PASSWORD|SECRET|KEY|TOKEN|API_KEY)\s*=/i;
if (secretPattern.test(content)) {
issues.push({ severity: "HIGH", message: "Sensitive variable (PASSWORD/SECRET/KEY) hardcoded in ENV/ARG" });
}
// 5. No .dockerignore warning (check file exists)
if (!fs.existsSync(".dockerignore")) {
issues.push({ severity: "LOW", message: ".dockerignore missing โ€” may include node_modules or .env in image" });
}
// 6. npm install without --omit=dev in production
if (content.includes("npm install") && !content.includes("--omit=dev") && !content.includes("--only=production")) {
issues.push({ severity: "LOW", message: "'npm install' without --omit=dev โ€” devDependencies included in image" });
}
// 7. Multi-stage โ€” good practice check
const fromCount = (content.match(/^FROM\s/gim) ?? []).length;
if (fromCount === 1) {
issues.push({ severity: "LOW", message: "Single-stage build โ€” consider multi-stage to reduce image size" });
}
return issues;
}
export async function scanDocker(options: { file: string; compose?: string; json?: boolean }) {
const issues: DockerIssue[] = [];
if (fs.existsSync(options.file)) {
const content = fs.readFileSync(options.file, "utf-8");
issues.push(...analyzeDockerfile(content));
} else {
console.log(chalk.yellow(`โš ๏ธ Dockerfile not found at ${options.file}`));
}
if (options.json) {
console.log(JSON.stringify({ issues, total: issues.length }, null, 2));
return;
}
console.log(chalk.bold.cyan("\n๐Ÿณ Docker Analysis\n"));
if (issues.length === 0) {
console.log(chalk.green("โœ… Dockerfile looks good!"));
return;
}
for (const issue of issues) {
const color = issue.severity === "HIGH" ? chalk.red : issue.severity === "MEDIUM" ? chalk.yellow : chalk.gray;
console.log(color(`[${issue.severity}] ${issue.message}`));
}
console.log();
}
import fs from "fs";
import chalk from "chalk";
export async function scanLogs(options: { json?: boolean }) {
const issues: string[] = [];
const passed: string[] = [];
// Check if Winston or Morgan installed
const pkgPath = "package.json";
if (!fs.existsSync(pkgPath)) {
console.log(chalk.yellow("โš ๏ธ package.json not found"));
return;
}
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")) as {
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
};
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
const hasWinston = "winston" in allDeps;
const hasMorgan = "morgan" in allDeps;
const hasPino = "pino" in allDeps;
if (!hasWinston && !hasMorgan && !hasPino) {
issues.push("No logger found (winston/morgan/pino) โ€” console.log is not production-grade");
} else {
if (hasWinston) passed.push("winston detected");
if (hasMorgan) passed.push("morgan detected");
if (hasPino) passed.push("pino detected");
}
// Check for winston transports (file rotation)
if (hasWinston) {
const hasRotation = "winston-daily-rotate-file" in allDeps;
if (!hasRotation) {
issues.push("winston-daily-rotate-file not found โ€” logs may grow unbounded");
} else {
passed.push("log rotation configured");
}
}
// Check for LOG_LEVEL env usage
const envFile = fs.existsSync(".env") ? fs.readFileSync(".env", "utf-8") : "";
if (!envFile.includes("LOG_LEVEL")) {
issues.push("LOG_LEVEL not set in .env โ€” logger may default to verbose in production");
}
if (options.json) {
console.log(JSON.stringify({ issues, passed }, null, 2));
return;
}
console.log(chalk.bold.cyan("\n๐Ÿ“‹ Logger Health Check\n"));
for (const p of passed) console.log(chalk.green(`โœ… ${p}`));
for (const i of issues) console.log(chalk.yellow(`โš ๏ธ ${i}`));
console.log();
}
import { execSync } from "child_process";
import chalk from "chalk";
const KNOWN_SAFE_PORTS = [22, 80, 443, 3000, 3001, 4000, 5000, 8080, 8443];
const SUSPICIOUS_PORTS = [21, 23, 25, 445, 1433, 3306, 5432, 6379, 27017]; // ftp, telnet, smtp, smb, mssql, mysql, pg, redis, mongo
export async function scanPorts(options: { json?: boolean }) {
let output = "";
try {
const isWindows = process.platform === "win32";
output = isWindows
? execSync("netstat -ano", { encoding: "utf-8" })
: execSync("ss -tulnp 2>/dev/null || netstat -tulnp 2>/dev/null", {
encoding: "utf-8",
});
} catch {
console.log(chalk.yellow("โš ๏ธ Could not read port info"));
return;
}
const lines = output
.split("\n")
.filter((l) => l.includes("LISTEN") || l.includes("0.0.0.0"));
const openPorts: { port: number; process: string; warning?: string }[] = [];
for (const line of lines) {
const portMatch = line.match(/:(\d+)\s/);
if (!portMatch) continue;
const port = parseInt(portMatch[1] ?? "0");
const isExposed = line.includes("0.0.0.0") || line.includes("*:");
const isSuspicious = SUSPICIOUS_PORTS.includes(port) && isExposed;
const entry: { port: number; process: string; warning?: string } = {
port,
process: line.split(/\s+/).pop() ?? "unknown",
};
if (isSuspicious)
entry.warning = `Port ${port} is publicly exposed โ€” restrict to localhost`;
openPorts.push(entry);
}
if (options.json) {
console.log(JSON.stringify({ openPorts }, null, 2));
return;
}
console.log(chalk.bold.cyan("\n๐Ÿ”Œ Port & Process Monitor\n"));
for (const p of openPorts) {
if (p.warning) {
console.log(chalk.red(`[WARN] :${p.port} โ€” ${p.warning}`));
} else {
console.log(chalk.green(`[OK] :${p.port}`));
}
}
console.log();
}
import fs from "fs";
import path from "path";
import chalk from "chalk";
// Secret patterns โ€” no AI needed
const SECRET_PATTERNS: { name: string; pattern: RegExp; severity: "HIGH" | "MEDIUM" | "LOW" }[] = [
{ name: "AWS Access Key", pattern: /AKIA[0-9A-Z]{16}/, severity: "HIGH" },
{ name: "AWS Secret Key", pattern: /aws_secret_access_key\s*=\s*[^\s]{20,}/i, severity: "HIGH" },
{ name: "Private Key Block", pattern: /-----BEGIN (RSA |EC )?PRIVATE KEY-----/, severity: "HIGH" },
{ name: "Groq API Key", pattern: /gsk_[a-zA-Z0-9]{40,}/, severity: "HIGH" },
{ name: "OpenAI API Key", pattern: /sk-[a-zA-Z0-9]{32,}/, severity: "HIGH" },
{ name: "GitHub Token", pattern: /ghp_[a-zA-Z0-9]{36}/, severity: "HIGH" },
{ name: "JWT Secret hardcoded", pattern: /jwt[_-]?secret\s*[:=]\s*['"][^'"]{8,}/i, severity: "HIGH" },
{ name: "Database URL", pattern: /mongodb(\+srv)?:\/\/[^:]+:[^@]+@/i, severity: "HIGH" },
{ name: "Database URL (PG)", pattern: /postgres(ql)?:\/\/[^:]+:[^@]+@/i, severity: "HIGH" },
{ name: "Stripe Secret Key", pattern: /sk_live_[a-zA-Z0-9]{24,}/, severity: "HIGH" },
{ name: "Razorpay Secret", pattern: /rzp_live_[a-zA-Z0-9]{14,}/, severity: "HIGH" },
{ name: "Generic Password", pattern: /password\s*[:=]\s*['"][^'"]{6,}/i, severity: "MEDIUM" },
{ name: "Generic Secret", pattern: /secret\s*[:=]\s*['"][^'"]{6,}/i, severity: "MEDIUM" },
{ name: ".env file committed", pattern: /^\.env$/, severity: "HIGH" }, // filename check
];
const IGNORE_DIRS = ["node_modules", ".git", "dist", ".next", "build", "coverage"];
const IGNORE_FILES = [".env", ".env.local", ".env.development", ".env.production"];
const SCAN_EXTENSIONS = [".ts", ".js", ".tsx", ".jsx", ".env", ".json", ".yaml", ".yml", ".sh"];
interface Finding {
file: string;
line: number;
pattern: string;
severity: "HIGH" | "MEDIUM" | "LOW";
preview: string;
}
function shouldIgnore(filePath: string): boolean {
const fileName = path.basename(filePath);
if (IGNORE_FILES.includes(fileName)) return true; // ๐Ÿ‘ˆ add this
return IGNORE_DIRS.some((dir) => filePath.includes(`/${dir}/`));
}
function checkEnvInGitignore(): void {
if (!fs.existsSync(".gitignore")) {
console.log(chalk.red("[HIGH] .gitignore missing โ€” .env may get committed to git"));
return;
}
const gitignore = fs.readFileSync(".gitignore", "utf-8");
if (!gitignore.includes(".env")) {
console.log(chalk.red("[HIGH] .env is NOT in .gitignore โ€” risk of secret exposure in git"));
} else {
console.log(chalk.green("โœ… .env is gitignored"));
}
}
function scanFile(filePath: string): Finding[] {
const findings: Finding[] = [];
const ext = path.extname(filePath);
if (!SCAN_EXTENSIONS.includes(ext) && !filePath.endsWith(".env")) return findings;
if (shouldIgnore(filePath)) return findings;
const lines = fs.readFileSync(filePath, "utf-8").split("\n");
lines.forEach((line, idx) => {
for (const { name, pattern, severity } of SECRET_PATTERNS) {
if (pattern.test(line)) {
findings.push({
file: filePath,
line: idx + 1,
pattern: name,
severity,
preview: line.trim().slice(0, 80),
});
break;
}
}
});
return findings;
}
function walkDir(dir: string): string[] {
const files: string[] = [];
if (!fs.existsSync(dir)) return files;
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory() && !IGNORE_DIRS.includes(entry.name)) {
files.push(...walkDir(fullPath));
} else if (entry.isFile()) {
files.push(fullPath);
}
}
return files;
}
export async function scanSecrets(options: { dir: string; json?: boolean; ai?: boolean }) {
const targetDir = path.resolve(options.dir);
const allFiles = walkDir(targetDir);
const allFindings: Finding[] = [];
for (const file of allFiles) {
allFindings.push(...scanFile(file));
}
if (options.json) {
console.log(JSON.stringify({ findings: allFindings, total: allFindings.length }, null, 2));
return;
}
console.log(chalk.bold.cyan("\n๐Ÿ” Secrets Scanner\n"));
checkEnvInGitignore();
if (allFindings.length === 0) {
console.log(chalk.green("โœ… No secrets detected!"));
return;
}
const highCount = allFindings.filter((f) => f.severity === "HIGH").length;
const medCount = allFindings.filter((f) => f.severity === "MEDIUM").length;
console.log(chalk.red(`โŒ ${highCount} HIGH`) + " " + chalk.yellow(`โš ๏ธ ${medCount} MEDIUM`) + "\n");
for (const f of allFindings) {
const color = f.severity === "HIGH" ? chalk.red : chalk.yellow;
console.log(color(`[${f.severity}] ${f.pattern}`));
console.log(` File : ${f.file}:${f.line}`);
console.log(` Code : ${chalk.gray(f.preview)}\n`);
}
if (options.ai) {
const key = process.env["GROQ_API_KEY"];
if (!key) {
console.log(chalk.gray("โ„น๏ธ AI insights skipped โ€” GROQ_API_KEY not set"));
} else {
// dynamic import to avoid breaking non-AI usage
const { analyzeWithAI } = await import("../utils/ai.js");
const summary = await analyzeWithAI(
allFindings.map((f) => `${f.pattern} in ${f.file}:${f.line}`).join("\n")
);
console.log(chalk.cyan("\n๐Ÿ’ก AI Insights\n") + summary);
}
}
}
+52
-10
#!/usr/bin/env node
import { Command } from "commander";
import { analyzeProject } from "./commands/analyze.js";
import { scanSecrets } from "./commands/scan-secrets.js";
import { scanDocker } from "./commands/scan-docker.js";
import { scanCI } from "./commands/scan-ci.js";
import { scanPorts } from "./commands/scan-ports.js";
import { scanLogs } from "./commands/scan-logs.js";
import { scanAll } from "./commands/scan-all.js";
const program = new Command();
program
.name("dep-inspector")
.description("Advanced dependency analyzer with AI-powered insights")
.version("1.0.0");
// Shared options for both default and analyze command
function addOptions(cmd) {
.description("DevOps-grade dependency & security toolkit")
.version("2.0.0");
// V1 โ€” existing
function addAnalyzeOptions(cmd) {
return cmd
.option("--ai", "Enable AI insights via Groq")
.option("--json", "Output results as JSON (useful for CI/CD)")
.option("--ai", "Enable AI insights via Groq (optional)")
.option("--json", "Output as JSON")
.option("--depth <number>", "Limit dependency tree depth", parseInt);
}
// Default action โ€” runs when user just types: dep-inspector
addOptions(program).action((options) => analyzeProject(options));
// Explicit analyze subcommand โ€” dep-inspector analyze
addOptions(program.command("analyze").description("Analyze project dependencies")).action((options) => analyzeProject(options));
addAnalyzeOptions(program).action((options) => analyzeProject(options));
addAnalyzeOptions(program.command("analyze").description("Analyze dependencies (V1)")).action((options) => analyzeProject(options));
// V2 โ€” new scan commands
program
.command("scan:secrets")
.description("Scan for hardcoded secrets, API keys, .env leaks")
.option("--dir <path>", "Directory to scan", ".")
.option("--json", "Output as JSON")
.option("--ai", "AI-powered explanation (optional, needs GROQ_API_KEY)")
.action((options) => scanSecrets(options));
program
.command("scan:docker")
.description("Analyze Dockerfile and docker-compose.yml for issues")
.option("--file <path>", "Path to Dockerfile", "Dockerfile")
.option("--compose <path>", "Path to docker-compose file")
.option("--json", "Output as JSON")
.action((options) => scanDocker(options));
program
.command("scan:ci")
.description("Lint GitHub Actions workflows for security & best practices")
.option("--dir <path>", "Workflows directory", ".github/workflows")
.option("--json", "Output as JSON")
.action((options) => scanCI(options));
program
.command("scan:ports")
.description("Check open ports and running processes")
.option("--json", "Output as JSON")
.action((options) => scanPorts(options));
program
.command("scan:logs")
.description("Check Winston/Morgan logger configuration health")
.option("--json", "Output as JSON")
.action((options) => scanLogs(options));
program
.command("scan:all")
.description("Run all scans and generate a full report")
.option("--ai", "AI summary (optional)")
.option("--json", "Output as JSON")
.option("--report", "Save HTML report to dep-inspector-report.html")
.action((options) => scanAll(options));
program.parse();
//# sourceMappingURL=index.js.map
+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,uDAAuD,CAAC;KACpE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,sDAAsD;AACtD,SAAS,UAAU,CAAC,GAAY;IAC9B,OAAO,GAAG;SACP,MAAM,CAAC,MAAM,EAAE,6BAA6B,CAAC;SAC7C,MAAM,CAAC,QAAQ,EAAE,2CAA2C,CAAC;SAC7D,MAAM,CAAC,kBAAkB,EAAE,6BAA6B,EAAE,QAAQ,CAAC,CAAC;AACzE,CAAC;AAED,4DAA4D;AAC5D,UAAU,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAEjE,sDAAsD;AACtD,UAAU,CACR,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,WAAW,CAAC,8BAA8B,CAAC,CACvE,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AAE/C,OAAO,CAAC,KAAK,EAAE,CAAC"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}

@@ -1,1 +0,1 @@

{"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/utils/ai.ts"],"names":[],"mappings":"AAaA,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsClE"}
{"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/utils/ai.ts"],"names":[],"mappings":"AAOA,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmClE"}

@@ -1,24 +0,18 @@

import { ChatGroq } from "@langchain/groq";
import Groq from "groq-sdk";
import { config } from "dotenv";
config();
const model = new ChatGroq({
model: "llama-3.1-8b-instant",
temperature: 0.3,
});
// In-memory cache
const cache = new Map();
export async function analyzeWithAI(input) {
try {
if (cache.has(input)) {
if (cache.has(input))
return cache.get(input);
}
const client = new Groq({ apiKey: process.env["GROQ_API_KEY"] });
const prompt = `
You are a senior software security engineer and tester with high experience of at least 10 years.
Make sure to provide detailed and actionable insights for each issue, including potential impacts, security risks, and recommended fixes or alternatives.
You are a senior software security engineer with 10+ years of experience.
Provide detailed and actionable insights for each issue found.
Analyze the following dependency issues:
Analyze the following:
${input}
Respond in this format:
1. Issue

@@ -30,12 +24,11 @@ 2. Impact

`;
const res = await model.invoke(prompt);
let finalOutput = "";
if (typeof res.content === "string") {
finalOutput = res.content.trim();
}
else {
finalOutput = JSON.stringify(res.content, null, 2);
}
cache.set(input, finalOutput);
return finalOutput;
const res = await client.chat.completions.create({
model: "llama-3.3-70b-versatile",
messages: [{ role: "user", content: prompt }],
max_tokens: 500,
temperature: 0.3,
});
const output = res.choices[0]?.message?.content?.trim() ?? "โš ๏ธ No response";
cache.set(input, output);
return output;
}

@@ -42,0 +35,0 @@ catch {

@@ -1,1 +0,1 @@

{"version":3,"file":"ai.js","sourceRoot":"","sources":["../../src/utils/ai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,MAAM,EAAE,CAAC;AAET,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC;IACzB,KAAK,EAAE,sBAAsB;IAC7B,WAAW,EAAE,GAAG;CACjB,CAAC,CAAC;AAEH,kBAAkB;AAClB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAa;IAC/C,IAAI,CAAC;QACH,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC;QAC3B,CAAC;QAED,MAAM,MAAM,GAAG;;;;;EAKjB,KAAK;;;;;;;;;CASN,CAAC;QAEE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEvC,IAAI,WAAW,GAAG,EAAE,CAAC;QAErB,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACpC,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACrD,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAE9B,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,wBAAwB,CAAC;IAClC,CAAC;AACH,CAAC"}
{"version":3,"file":"ai.js","sourceRoot":"","sources":["../../src/utils/ai.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,UAAU,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,MAAM,EAAE,CAAC;AAET,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAa;IAC/C,IAAI,CAAC;QACH,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC;QAE/C,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;QAEjE,MAAM,MAAM,GAAG;;;;;EAKjB,KAAK;;;;;;;;CAQN,CAAC;QAEE,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YAC/C,KAAK,EAAE,yBAAyB;YAChC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;YAC7C,UAAU,EAAE,GAAG;YACf,WAAW,EAAE,GAAG;SACjB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,gBAAgB,CAAC;QAC5E,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACzB,OAAO,MAAM,CAAC;IAEhB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,wBAAwB,CAAC;IAClC,CAAC;AACH,CAAC"}
{
"name": "dep-inspector-cli",
"version": "1.1.0",
"description": "AI powered dependency inspector for JavaScript/TypeScript projects",
"version": "2.0.0",
"description": "DevOps-grade dependency, security & infrastructure scanner for Node.js projects",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"keywords": [
"dependency",
"inspector",
"analyzer",
"security",
"vulnerability",
"ai",
"groq",
"langchain"
],
"dependency",
"inspector",
"analyzer",
"security",
"vulnerability",
"devops",
"security-scanner",
"secrets-scanner",
"docker",
"cli",
"ci-cd",
"github-actions",
"groq",
"ai",
"nodejs"
],
"scripts": {
"build": "tsc",
"dev": "ts-node src/index.ts",
"test": "echo \"Error: no test specified\" && exit 1"

@@ -24,7 +33,6 @@ },

"dependencies": {
"@langchain/groq": "^1.2.0",
"chalk": "^5.6.2",
"commander": "^14.0.3",
"dotenv": "^17.4.2",
"langchain": "^1.3.3",
"groq-sdk": "^1.2.0",
"ora": "^9.3.0",

@@ -31,0 +39,0 @@ "semver": "^7.7.4",

+170
-101
# dep-inspector-cli
> Advanced CLI tool to analyze your Node.js project dependencies โ€” with vulnerability scanning, outdated version detection, dependency chain tracing, and optional AI-powered insights via Groq.
> DevOps-grade dependency, security & infrastructure scanner for Node.js projects.

@@ -9,17 +9,27 @@ [![npm version](https://img.shields.io/npm/v/dep-inspector-cli)](https://www.npmjs.com/package/dep-inspector-cli)

[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://www.typescriptlang.org/)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Nevin100/Dep-inspector-nevin/pulls)
---
## What it does
## What's new in v2
- ๐ŸŒณ **Dependency tree** โ€” visual tree of all your dependencies with outdated version highlights
- ๐Ÿ›ก๏ธ **Vulnerability scan** โ€” runs `npm audit` and surfaces issues with severity levels
- ๐Ÿ”— **Dependency chains** โ€” traces exactly which package pulled in a vulnerable dependency
- ๐Ÿ“ฆ **Package info** โ€” shows homepage, author, repo link for each flagged package
- ๐Ÿ”„ **Alternatives** โ€” suggests modern replacements for deprecated/risky packages
- ๐Ÿค– **AI insights** โ€” optional Groq-powered analysis with actionable fix suggestions
- ๐Ÿ“„ **JSON output** โ€” machine-readable output for CI/CD pipelines
Version 2 transforms `dep-inspector` from a dependency analyzer into a full **DevOps security toolkit** โ€” covering secrets, Docker, CI/CD pipelines, ports, and logging. All features work **without any API key**. AI insights are optional.
---
## Features
| Command | What it does |
|---|---|
| `dep-inspector` | Dependency tree + vulnerability scan (v1) |
| `scan:secrets` | Detect hardcoded API keys, .env leaks, private keys |
| `scan:vulns` | npm audit wrapper with severity thresholds |
| `scan:docker` | Dockerfile & docker-compose security analysis |
| `scan:ci` | GitHub Actions workflow linting |
| `scan:ports` | Open port detection & process monitoring |
| `scan:logs` | Winston/Morgan/Pino logger health check |
| `scan:all` | Run everything, generate a full report |
---
## Installation

@@ -35,23 +45,33 @@

Run from your project root (where `package.json` lives):
### Dependency Analysis (v1)
```bash
# Basic analysis
dep-inspector
dep-inspector # Full analysis
dep-inspector analyze # Same, explicit subcommand
dep-inspector --depth 3 # Limit tree depth
dep-inspector --json # Machine-readable output
dep-inspector --ai # AI-powered insights (optional, needs GROQ_API_KEY)
```
# Same as above using explicit subcommand
dep-inspector analyze
### Security Scans (v2)
# Enable AI-powered insights (requires GROQ_API_KEY)
dep-inspector --ai
```bash
dep-inspector scan:secrets # Scan current directory
dep-inspector scan:secrets --dir ./src # Scan specific directory
dep-inspector scan:secrets --ai # With AI explanations (optional)
# Output as JSON (great for CI/CD pipelines)
dep-inspector --json
dep-inspector scan:docker # Analyze Dockerfile
dep-inspector scan:docker --file ./docker/Dockerfile
# Limit dependency tree depth (useful for large projects)
dep-inspector --depth 3
dep-inspector scan:ci # Lint GitHub Actions workflows
dep-inspector scan:ci --dir .github/workflows
# Combine flags
dep-inspector --ai --depth 2
dep-inspector analyze --json --depth 4
dep-inspector scan:ports # Check open ports
dep-inspector scan:logs # Logger health check
dep-inspector scan:all # Full DevOps scan
dep-inspector scan:all --report # + saves HTML report
dep-inspector scan:all --json # + JSON output
dep-inspector scan:all --ai # + AI summary (optional)
```

@@ -61,49 +81,53 @@

## Commands & Flags
## What gets detected
| Command / Flag | Description |
|---|---|
| `dep-inspector` | Run full analysis (default) |
| `dep-inspector analyze` | Explicit analyze subcommand |
| `--ai` | Enable AI analysis via Groq LLM |
| `--json` | Output results as JSON |
| `--depth <n>` | Limit dependency tree to n levels deep |
### `scan:secrets`
- AWS Access Keys & Secret Keys
- OpenAI, Groq, GitHub tokens
- Hardcoded JWT secrets
- MongoDB / PostgreSQL connection strings
- Stripe & Razorpay live keys
- Generic `password=` / `secret=` assignments
- Accidentally committed `.env` files
---
### `scan:docker`
- Container running as root (no `USER` directive)
- Missing `HEALTHCHECK`
- `:latest` tag usage (non-reproducible builds)
- Secrets hardcoded in `ENV`/`ARG`
- Missing `.dockerignore`
- `npm install` without `--omit=dev` in production
- Single-stage builds (image size warning)
## Example Output
### `scan:ci`
- Hardcoded secrets in workflow YAML
- Deprecated `::set-output` command
- `pull_request_target` + `actions/checkout` (privilege escalation risk)
- Actions pinned to `@main` / `@latest` instead of a version
- Missing dependency cache
- No `timeout-minutes` (stuck jobs)
```
๐ŸŒณ Dependency Tree
### `scan:ports`
- Lists all open/listening ports
- Flags database ports (Redis, MongoDB, PostgreSQL, MySQL) publicly exposed
- Flags FTP, Telnet, and other insecure services
โ””โ”€โ”€ my-project@1.0.0
โ”œโ”€โ”€ axios@0.21.1 (latest: 1.7.2) โŒ HIGH
โ”œโ”€โ”€ express@4.18.2
โ”‚ โ”œโ”€โ”€ body-parser@1.20.1
โ”‚ โ””โ”€โ”€ serve-static@1.15.0
โ””โ”€โ”€ lodash@4.17.21
### `scan:logs`
- Detects missing logger (console.log in production)
- Checks for `winston-daily-rotate-file` (log rotation)
- Validates `LOG_LEVEL` environment variable
โš ๏ธ Vulnerability Analysis
---
๐Ÿ“ฆ axios
Severity : HIGH
Version : 0.21.1 โ†’ 1.7.2
โš ๏ธ Breaking change possible!
About : Promise based HTTP client
Docs : https://axios-http.com
Author : Matt Zabriskie
Repo : https://github.com/axios/axios
Alternative: โ€”
Tests :
- Test API calls
- Validate request/response headers
๐Ÿ”— Chain: root โ†’ axios
## Output
๐Ÿ’ก Fix Suggestions
All commands support `--json` for machine-readable output:
โ†’ axios: npm install axios@latest
โœจ Analysis Complete
```bash
dep-inspector scan:secrets --json > secrets-report.json
dep-inspector scan:all --json > full-report.json
```
Severity levels: `HIGH` ยท `MEDIUM` ยท `LOW`
---

@@ -113,16 +137,13 @@

The `--ai` flag uses [Groq](https://console.groq.com) to provide detailed security analysis for each vulnerable package.
The `--ai` flag sends findings to Groq LLM for human-readable explanations and fix suggestions. It is **completely optional** โ€” every scan works without it.
1. Get a free API key at [console.groq.com](https://console.groq.com)
2. Create a `.env` file in your project root:
```bash
# Set once in your shell profile or .env
export GROQ_API_KEY=your_key_here
```env
GROQ_API_KEY=your_groq_api_key_here
dep-inspector scan:secrets --ai
dep-inspector scan:all --ai
```
The AI will provide:
- Issue explanation
- Security impact assessment
- Recommended fix
- Alternative package suggestions
Get a free key at [console.groq.com](https://console.groq.com). If the key is missing, the tool runs normally and skips AI output with a note.

@@ -133,20 +154,44 @@ ---

Use `--json` flag to integrate into your pipeline:
```yaml
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
dep-inspector:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dep-inspector
run: npm install -g dep-inspector-cli
- name: Scan secrets
run: dep-inspector scan:secrets --json > secrets.json
- name: Scan dependencies
run: dep-inspector --json > deps.json
- name: Lint CI workflows
run: dep-inspector scan:ci
- name: Upload reports
uses: actions/upload-artifact@v4
with:
name: dep-inspector-reports
path: "*.json"
```
**Fail build on HIGH severity secrets:**
```bash
# Generate JSON report
dep-inspector --json > dep-report.json
# Fail build if any vulnerabilities found
dep-inspector --json | node -e "
let data = '';
process.stdin.on('data', d => data += d);
dep-inspector scan:secrets --json | node -e "
let d = '';
process.stdin.on('data', c => d += c);
process.stdin.on('end', () => {
const r = JSON.parse(data);
const count = Object.keys(r.vulnerabilities).length;
if (count > 0) {
console.error('Found ' + count + ' vulnerabilities. Failing build.');
process.exit(1);
}
console.log('No vulnerabilities found.');
const { findings } = JSON.parse(d);
const high = findings.filter(f => f.severity === 'HIGH').length;
if (high > 0) { console.error(high + ' HIGH severity secrets found. Failing build.'); process.exit(1); }
console.log('No HIGH severity secrets found.');
});

@@ -156,14 +201,27 @@ "

**GitHub Actions example:**
---
```yaml
- name: Check dependencies
run: dep-inspector --json > dep-report.json
## Project Structure
- name: Upload report
uses: actions/upload-artifact@v3
with:
name: dependency-report
path: dep-report.json
```
dep-inspector/
โ”œโ”€โ”€ src/
โ”‚ โ”œโ”€โ”€ index.ts # CLI entry โ€” all commands registered here
โ”‚ โ”œโ”€โ”€ commands/
โ”‚ โ”‚ โ”œโ”€โ”€ analyze.ts # v1 dependency analysis
โ”‚ โ”‚ โ”œโ”€โ”€ scan-secrets.ts # secrets & key scanner
โ”‚ โ”‚ โ”œโ”€โ”€ scan-docker.ts # Dockerfile analysis
โ”‚ โ”‚ โ”œโ”€โ”€ scan-ci.ts # GitHub Actions linter
โ”‚ โ”‚ โ”œโ”€โ”€ scan-ports.ts # port monitor
โ”‚ โ”‚ โ”œโ”€โ”€ scan-logs.ts # logger health check
โ”‚ โ”‚ โ””โ”€โ”€ scan-all.ts # full scan orchestrator
โ”‚ โ””โ”€โ”€ utils/
โ”‚ โ”œโ”€โ”€ ai.ts # optional Groq integration
โ”‚ โ”œโ”€โ”€ audit.ts # npm audit wrapper
โ”‚ โ”œโ”€โ”€ deps.ts # npm ls wrapper
โ”‚ โ”œโ”€โ”€ tree.ts # tree printer
โ”‚ โ””โ”€โ”€ version.ts # semver comparison
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ tsconfig.json
```

@@ -175,4 +233,4 @@ ---

- Node.js >= 16
- npm (must be available in PATH)
- `GROQ_API_KEY` in `.env` (only required for `--ai` flag)
- npm in PATH
- `GROQ_API_KEY` โ€” only needed for `--ai` flag

@@ -183,7 +241,7 @@ ---

- **TypeScript** โ€” fully typed codebase
- **TypeScript** โ€” fully typed
- **Commander.js** โ€” CLI argument parsing
- **Chalk** โ€” colored terminal output
- **Ora** โ€” terminal spinner
- **LangChain + Groq** โ€” AI analysis
- **Chalk** โ€” colored output
- **Ora** โ€” terminal spinners
- **Groq SDK** โ€” optional AI layer (direct, no LangChain dependency)
- **Semver** โ€” version comparison

@@ -193,6 +251,15 @@

## Roadmap
- [ ] `scan:secrets` โ€” `.git` history scanning (catch keys that were deleted but committed)
- [ ] `scan:docker` โ€” docker-compose multi-service analysis
- [ ] `--report` โ€” full HTML report with charts
- [ ] Slack / Discord webhook alerts
- [ ] GitHub App integration (PR comments)
- [ ] Custom rule config via `.depinspectorrc`
---
## Contributing
Pull requests are welcome! For major changes, please open an issue first.
```bash

@@ -205,2 +272,4 @@ git clone https://github.com/Nevin100/Dep-inspector-nevin

Pull requests welcome. For major changes, open an issue first.
---

@@ -207,0 +276,0 @@

@@ -5,2 +5,8 @@ #!/usr/bin/env node

import { analyzeProject } from "./commands/analyze.js";
import { scanSecrets } from "./commands/scan-secrets.js";
import { scanDocker } from "./commands/scan-docker.js";
import { scanCI } from "./commands/scan-ci.js";
import { scanPorts } from "./commands/scan-ports.js";
import { scanLogs } from "./commands/scan-logs.js";
import { scanAll } from "./commands/scan-all.js";

@@ -11,21 +17,62 @@ const program = new Command();

.name("dep-inspector")
.description("Advanced dependency analyzer with AI-powered insights")
.version("1.0.0");
// Shared options for both default and analyze command
function addOptions(cmd: Command) {
.description("DevOps-grade dependency & security toolkit")
.version("2.0.0");
// V1 โ€” existing
function addAnalyzeOptions(cmd: Command) {
return cmd
.option("--ai", "Enable AI insights via Groq")
.option("--json", "Output results as JSON (useful for CI/CD)")
.option("--ai", "Enable AI insights via Groq (optional)")
.option("--json", "Output as JSON")
.option("--depth <number>", "Limit dependency tree depth", parseInt);
}
// Default action โ€” runs when user just types: dep-inspector
addOptions(program).action((options) => analyzeProject(options));
// Explicit analyze subcommand โ€” dep-inspector analyze
addOptions(
program.command("analyze").description("Analyze project dependencies")
addAnalyzeOptions(program).action((options) => analyzeProject(options));
addAnalyzeOptions(
program.command("analyze").description("Analyze dependencies (V1)")
).action((options) => analyzeProject(options));
// V2 โ€” new scan commands
program
.command("scan:secrets")
.description("Scan for hardcoded secrets, API keys, .env leaks")
.option("--dir <path>", "Directory to scan", ".")
.option("--json", "Output as JSON")
.option("--ai", "AI-powered explanation (optional, needs GROQ_API_KEY)")
.action((options) => scanSecrets(options));
program
.command("scan:docker")
.description("Analyze Dockerfile and docker-compose.yml for issues")
.option("--file <path>", "Path to Dockerfile", "Dockerfile")
.option("--compose <path>", "Path to docker-compose file")
.option("--json", "Output as JSON")
.action((options) => scanDocker(options));
program
.command("scan:ci")
.description("Lint GitHub Actions workflows for security & best practices")
.option("--dir <path>", "Workflows directory", ".github/workflows")
.option("--json", "Output as JSON")
.action((options) => scanCI(options));
program
.command("scan:ports")
.description("Check open ports and running processes")
.option("--json", "Output as JSON")
.action((options) => scanPorts(options));
program
.command("scan:logs")
.description("Check Winston/Morgan logger configuration health")
.option("--json", "Output as JSON")
.action((options) => scanLogs(options));
program
.command("scan:all")
.description("Run all scans and generate a full report")
.option("--ai", "AI summary (optional)")
.option("--json", "Output as JSON")
.option("--report", "Save HTML report to dep-inspector-report.html")
.action((options) => scanAll(options));
program.parse();

@@ -1,2 +0,2 @@

import { ChatGroq } from "@langchain/groq";
import Groq from "groq-sdk";
import { config } from "dotenv";

@@ -6,8 +6,2 @@

const model = new ChatGroq({
model: "llama-3.1-8b-instant",
temperature: 0.3,
});
// In-memory cache
const cache = new Map<string, string>();

@@ -17,15 +11,14 @@

try {
if (cache.has(input)) {
return cache.get(input)!;
}
if (cache.has(input)) return cache.get(input)!;
const client = new Groq({ apiKey: process.env["GROQ_API_KEY"] });
const prompt = `
You are a senior software security engineer and tester with high experience of at least 10 years.
Make sure to provide detailed and actionable insights for each issue, including potential impacts, security risks, and recommended fixes or alternatives.
You are a senior software security engineer with 10+ years of experience.
Provide detailed and actionable insights for each issue found.
Analyze the following dependency issues:
Analyze the following:
${input}
Respond in this format:
1. Issue

@@ -38,15 +31,13 @@ 2. Impact

const res = await model.invoke(prompt);
const res = await client.chat.completions.create({
model: "llama-3.3-70b-versatile",
messages: [{ role: "user", content: prompt }],
max_tokens: 500,
temperature: 0.3,
});
let finalOutput = "";
const output = res.choices[0]?.message?.content?.trim() ?? "โš ๏ธ No response";
cache.set(input, output);
return output;
if (typeof res.content === "string") {
finalOutput = res.content.trim();
} else {
finalOutput = JSON.stringify(res.content, null, 2);
}
cache.set(input, finalOutput);
return finalOutput;
} catch {

@@ -53,0 +44,0 @@ return "โš ๏ธ AI analysis failed.";