UNPKG

securesync

Version:

Intelligent dependency security scanner with auto-fix

26 lines (21 loc) 17.8 kB
#!/usr/bin/env node "use strict";var oe=Object.create;var S=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var ue=Object.getPrototypeOf,pe=Object.prototype.hasOwnProperty;var de=(e,n)=>{for(var t in n)S(e,t,{get:n[t],enumerable:!0})},J=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of le(n))!pe.call(e,i)&&i!==t&&S(e,i,{get:()=>n[i],enumerable:!(r=ce(n,i))||r.enumerable});return e};var v=(e,n,t)=>(t=e!=null?oe(ue(e)):{},J(n||!e||!e.__esModule?S(t,"default",{value:e,enumerable:!0}):t,e)),fe=e=>J(S({},"__esModule",{value:!0}),e);var Xe={};de(Xe,{SecureSync:()=>R,analyzeBreakingChanges:()=>$,analyzeVersionDiff:()=>k,buildGraph:()=>b,findAlternatives:()=>M,findDependencyPath:()=>V,generateMigration:()=>D,getDepth:()=>E,getDirectDependencies:()=>U,getTransitiveDependencies:()=>_,parseChangelog:()=>x,printSummary:()=>L,runTests:()=>P,scanNpmProject:()=>w,scoreAlternative:()=>A,testDrivenUpdate:()=>C,visualize:()=>T});module.exports=fe(Xe);var B=require("fs/promises"),I=require("path"),N=require("fs");async function w(e,n={}){let t=await ge(e),r=await me(e),i=ye(t,r,n.includeDevDependencies??!1),s=await be(e,i),o=n.enhanceWithOSV?await ve(s):s,a=n.analyzeReachability?await we(o,e):o,c={critical:a.filter(u=>u.severity==="critical").length,high:a.filter(u=>u.severity==="high").length,moderate:a.filter(u=>u.severity==="moderate").length,low:a.filter(u=>u.severity==="low").length};return{vulnerabilities:a,totalPackages:i.packages.length,scannedAt:new Date,dependencies:i,summary:c}}async function ge(e){let n=(0,I.join)(e,"package.json");if(!(0,N.existsSync)(n))throw new Error(`package.json not found at ${n}`);let t=await(0,B.readFile)(n,"utf-8");return JSON.parse(t)}async function me(e){let n=(0,I.join)(e,"package-lock.json");if(!(0,N.existsSync)(n))throw new Error(`package-lock.json not found at ${n}. Please run 'npm install' first.`);let t=await(0,B.readFile)(n,"utf-8");return JSON.parse(t)}function ye(e,n,t){let r=[],i=new Map,s=e.dependencies||{},o=t?e.devDependencies||{}:{};for(let[a]of Object.entries(s)){let c=n.packages?.[`node_modules/${a}`];c&&(i.set(a,{name:a,version:c.version,resolved:c.resolved||"",dependencies:z(n,c)}),r.push({name:a,version:c.version,isDevDependency:!1,isDirect:!0}))}for(let[a]of Object.entries(o)){let c=n.packages?.[`node_modules/${a}`];c&&(i.set(a,{name:a,version:c.version,resolved:c.resolved||"",dependencies:z(n,c)}),r.push({name:a,version:c.version,isDevDependency:!0,isDirect:!0}))}return he(i,r),{name:e.name,version:e.version,dependencies:i,packages:r}}function z(e,n){if(!n.dependencies)return;let t=new Map;for(let[r]of Object.entries(n.dependencies)){let i=e.packages?.[`node_modules/${r}`];i&&t.set(r,{name:r,version:i.version,resolved:i.resolved||"",dependencies:z(e,i)})}return t.size>0?t:void 0}function he(e,n){let t=new Set(n.map(i=>`${i.name}@${i.version}`));function r(i,s){if(i)for(let[o,a]of i){let c=`${o}@${a.version}`;t.has(c)||(t.add(c),n.push({name:o,version:a.version,isDevDependency:s,isDirect:!1})),r(a.dependencies,s)}}for(let[,i]of e)r(i.dependencies,!1)}async function be(e,n){let t=[];try{return t}catch(r){return console.error("Error querying npm audit:",r),t}}async function ve(e){return e}async function we(e,n){return e}var h=require("semver");function k(e,n){if(!(0,h.valid)(e)||!(0,h.valid)(n))throw new Error(`Invalid semver versions: ${e} or ${n}`);let t=(0,h.compare)(e,n),r=(0,h.diff)(e,n);return{fromVersion:e,toVersion:n,diffType:r,isUpgrade:t<0,isDowngrade:t>0,expectedBreakingChanges:r==="major"||r==="premajor"}}function H(e){return e.expectedBreakingChanges?!0:e.isUpgrade&&e.diffType!==null}async function x(e){let n=[],t=/^##?\s*\[?(\d+\.\d+\.\d+[^\]]*)\]?.*$/gm,r=[...e.matchAll(t)];for(let i=0;i<r.length;i++){let s=r[i],o=s[1],a=s.index+s[0].length,c=i<r.length-1?r[i+1].index:e.length,u=e.slice(a,c);n.push({version:o,date:ke(s[0]),changes:xe(u)})}return n}function ke(e){let n=/\d{4}-\d{2}-\d{2}/,t=e.match(n);return t?t[0]:void 0}function xe(e){let n={breaking:[],features:[],fixes:[],other:[]},t=e.split(` `),r="other";for(let i of t){let s=i.trim();if(/^###?\s*(breaking|breaking changes)/i.test(s)){r="breaking";continue}else if(/^###?\s*(features?|added)/i.test(s)){r="features";continue}else if(/^###?\s*(fix(es)?|bug\s*fix(es)?)/i.test(s)){r="fixes";continue}if(/^[-*]\s/.test(s)){let o=s.replace(/^[-*]\s/,"").trim();o&&n[r].push(o)}}return n}function q(e,n){let t=new Map,r=e.find(i=>i.version===n);if(!r)return t;for(let i of r.changes.breaking){let s=De(i);s&&t.set(s.from,s.to)}return t}function De(e){let n=/replace\s+`?(\w+)`?\s+with\s+`?(\w+)`?/i,t=/use\s+`?(\w+)`?\s+instead\s+of\s+`?(\w+)`?/i,r=e.match(n);return r?{from:r[1],to:r[2]}:(r=e.match(t),r?{from:r[2],to:r[1]}:null)}function K(e,n){let t=[],r=new Map(e.map(s=>[s.name,s])),i=new Map(n.map(s=>[s.name,s]));for(let[s,o]of r){if(!o.exported)continue;let a=i.get(s);a?a.signature!==o.signature&&t.push({type:"breaking",category:"signature",symbol:s,before:o.signature,after:a.signature,confidence:.9,source:"typescript"}):t.push({type:"breaking",category:"removed",symbol:s,before:o.signature,after:"",confidence:1,source:"typescript"})}for(let[s,o]of i)o.exported&&(r.has(s)||t.push({type:"feature",category:"signature",symbol:s,before:"",after:o.signature,confidence:1,source:"typescript"}));return t}async function O(e){return[]}function Q(e,n){return e.map(t=>{if(t.category==="removed"||t.category==="renamed"){let r=n.get(t.symbol);if(r)return{...t,migration:`Replace \`${t.symbol}\` with \`${r}\``,confidence:Math.min(t.confidence+.1,1)}}return t})}async function $(e,n,t){let r=k(n,t);if(!H(r))return{packageName:e,fromVersion:n,toVersion:t,changes:[],hasBreakingChanges:!1,riskLevel:"low",analyzedAt:new Date};let i=await W(e,n),s=await W(e,t),o=await O(i),a=await O(s),c=K(o,a),u=await Pe(s),l=u?await x(u):[],f=q(l,t),p=Q(c,f),d=l.find(m=>m.version===t);if(d)for(let m of d.changes.breaking)p.some(j=>j.symbol===m)||p.push({type:"breaking",category:"behavior",symbol:m,before:"",after:"",confidence:.7,source:"changelog"});let g=Ce(p,r.expectedBreakingChanges);return{packageName:e,fromVersion:n,toVersion:t,changes:p,hasBreakingChanges:p.some(m=>m.type==="breaking"),riskLevel:g,analyzedAt:new Date}}async function W(e,n){return`/tmp/securesync-cache/${e}@${n}`}async function Pe(e){return null}function Ce(e,n){let t=e.filter(r=>r.type==="breaking");return t.length===0?"low":t.length>5||!n?"high":"medium"}var X=require("fs/promises"),Y=require("@babel/parser"),Z=v(require("@babel/traverse")),F=v(require("@babel/generator"));async function D(e,n,t){let r=[],i=await Ae(e,n);for(let s of i){let o=await(0,X.readFile)(s,"utf-8"),a;try{a=(0,Y.parse)(o,{sourceType:"module",plugins:["typescript","jsx"]})}catch(l){console.warn(`Failed to parse ${s}:`,l);continue}let c=[],u=t.filter(l=>l.type==="breaking");(0,Z.default)(a,{CallExpression(l){let f=l.node.callee,p=Me(f,u);if(p){let d=(0,F.default)(l.node),g=Te(l.node,p);g&&c.push({line:l.node.loc?.start.line||0,column:l.node.loc?.start.column||0,old:d.code,new:g})}},ImportDeclaration(l){if(l.node.source.value===n){for(let f of l.node.specifiers)if(f.type==="ImportSpecifier"){let p=f.imported.type==="Identifier"?f.imported.name:"",d=u.find(g=>g.symbol===p&&g.category==="renamed");if(d&&d.migration){let g=(0,F.default)(f);c.push({line:f.loc?.start.line||0,column:f.loc?.start.column||0,old:g.code,new:d.migration})}}}}}),c.length>0&&r.push({file:s,changes:c,script:Se(s,c),safe:$e(c,u)})}return r}async function Ae(e,n){return[]}function Me(e,n){return e.type==="Identifier"?n.find(t=>t.symbol===e.name)||null:e.type==="MemberExpression"&&e.property.type==="Identifier"&&n.find(t=>t.symbol===e.property.name)||null}function Te(e,n){return n.migration?n.migration:(n.category==="signature",null)}function Se(e,n){let t=`// Migration script for ${e} `;t+=`import { readFile, writeFile } from 'fs/promises'; `,t+=`async function migrate() { `,t+=` const content = await readFile('${e}', 'utf-8'); `,t+=` let updated = content; `;for(let r of n)t+=` // Line ${r.line}: ${r.old} -> ${r.new} `,t+=` updated = updated.replace( `,t+=` ${JSON.stringify(r.old)}, `,t+=` ${JSON.stringify(r.new)} `,t+=` ); `;return t+=` await writeFile('${e}', updated, 'utf-8'); `,t+=` console.log('Migration completed for ${e}'); `,t+=`} `,t+=`migrate().catch(console.error); `,t}function $e(e,n){return!(e.length>5||n.some(i=>i.confidence<.8)||e.some(i=>!i.new||i.new===i.old))}var ne=require("child_process"),te=require("util"),re=require("fs/promises"),y=require("path"),G=(0,te.promisify)(ne.exec);async function C(e,n,t,r,i={}){let s=i.runTests??!0,o=i.createBackup??!0;if(s){let a=await P(e);if(!a.passed)return{success:!1,reason:"Tests failing before update - please fix existing test failures first",failedTests:a.failedTests}}o&&await ze(e);try{if(await Be(e,n,t),i.autoApply&&r.length>0)for(let a of r)(a.safe||i.interactive===!1)&&await Ie(a);if(s){let a=await P(e);if(!a.passed)return await ee(e),{success:!1,reason:"Tests failed after update",failedTests:a.failedTests,migrations:r,rolledBack:!0}}return{success:!0,migrations:r}}catch(a){throw o&&await ee(e),a}}async function P(e){let n=Date.now();try{let t=await Re(e);if(!t)return{passed:!0,output:"No test command found, skipping tests",duration:Date.now()-n};let{stdout:r}=await G(t,{cwd:e,env:{...process.env,CI:"true"}});return{passed:!0,output:r,duration:Date.now()-n,exitCode:0}}catch(t){return{passed:!1,output:t.stderr||t.stdout||t.message,duration:Date.now()-n,failedTests:je(t.stderr||t.stdout||""),exitCode:t.code}}}async function Re(e){try{let n=(0,y.join)(e,"package.json"),t=await(0,re.readFile)(n,"utf-8");return JSON.parse(t).scripts?.test||null}catch{return null}}function je(e){let n=[],t=[/FAIL\s+(.+)/g,/✖\s+(.+)/g,/×\s+(.+)/g,/ERROR\s+(.+)/g];for(let r of t){let i=e.matchAll(r);for(let s of i)s[1]&&!n.includes(s[1])&&n.push(s[1].trim())}return n}async function ze(e){let{copyFile:n}=await import("fs/promises");try{await n((0,y.join)(e,"package.json"),(0,y.join)(e,"package.json.backup")),await n((0,y.join)(e,"package-lock.json"),(0,y.join)(e,"package-lock.json.backup"))}catch(t){console.warn("Failed to create backup files:",t)}}async function Be(e,n,t){await G(`npm install ${n}@${t}`,{cwd:e})}async function Ie(e){let{readFile:n,writeFile:t}=await import("fs/promises");try{let r=await n(e.file,"utf-8");for(let i of e.changes)r=r.replace(i.old,i.new);await t(e.file,r,"utf-8")}catch(r){throw console.error(`Failed to apply migration to ${e.file}:`,r),r}}async function ee(e){let{copyFile:n,unlink:t}=await import("fs/promises");try{await n((0,y.join)(e,"package.json.backup"),(0,y.join)(e,"package.json")),await n((0,y.join)(e,"package-lock.json.backup"),(0,y.join)(e,"package-lock.json")),await G("npm install",{cwd:e}),await t((0,y.join)(e,"package.json.backup")),await t((0,y.join)(e,"package-lock.json.backup"))}catch(r){throw console.error("Failed to rollback changes:",r),r}}async function A(e,n){let t=0,r=Ne(e.downloads);t+=r*.3;let i=Oe(e.lastPublish);t+=i*.25;let s=await Fe(e.name);t+=s*.25;let o=await Ge(e);return t+=o*.2,Math.min(100,Math.round(t))}function Ne(e){return e>=1e6?100:e>=1e5?75+(e-1e5)/9e5*25:e>=1e4?50+(e-1e4)/9e4*25:e>=1e3?25+(e-1e3)/9e3*25:e/1e3*25}function Oe(e){let n=Ve(e);return n<30?100:n<90?80+(90-n)/60*20:n<180?60+(180-n)/90*20:n<365?40+(365-n)/185*20:n<730?20+(730-n)/365*20:Math.max(0,20-(n-730)/365*20)}async function Fe(e){return 100}async function Ge(e){let n=0;return e.description&&e.description.length>20&&(n+=20),e.repository&&(n+=20),e.homepage&&(n+=15),e.keywords&&e.keywords.length>=3&&(n+=15),e.license&&(n+=15),n}function Ve(e){let t=new Date().getTime()-e.getTime();return Math.floor(t/(1e3*60*60*24))}async function M(e,n={}){let t=await Ee(e),r=await Ue({keywords:t,exclude:[e]});return(await Promise.all(r.map(async s=>{let o=await A(s,e),a=await Le(s,e),c=Je(a,o);return{name:s.name,description:s.description,downloads:s.downloads,lastPublish:s.lastPublish,stars:0,issues:0,maintainers:0,vulnerabilities:0,compatibility:a,migrationEffort:c,score:o}}))).filter(s=>He(s,n)).sort((s,o)=>o.score-s.score).slice(0,10)}async function Ee(e){try{return(await _e(e)).keywords||[]}catch{return e.split("-").filter(n=>n.length>2)}}async function Ue(e){return[]}async function _e(e){return{name:e,description:"",version:"1.0.0",downloads:0,lastPublish:new Date,keywords:[]}}async function Le(e,n){return 50}function Je(e,n){return e>=80?"low":e>=50?"medium":"high"}function He(e,n){return!(n.minDownloads&&e.downloads<n.minDownloads||n.maxAge&&qe(e.lastPublish)>n.maxAge||n.minStars&&e.stars<n.minStars||n.zeroVulnerabilities&&e.vulnerabilities>0||n.minCompatibility&&e.compatibility<n.minCompatibility)}function qe(e){let t=new Date().getTime()-e.getTime();return Math.floor(t/(1e3*60*60*24))}function b(e){let n=new Map,t=[],r=[];for(let[i,s]of e.dependencies){let o=`${i}@${s.version}`;r.push(o),ie(s,o,0,void 0,n,t,!1)}return{nodes:n,edges:t,roots:r}}function ie(e,n,t,r,i,s,o){if(i.has(n)||i.set(n,{id:n,name:e.name,version:e.version,depth:t,parent:r,children:[],vulnerabilities:0,isDevDependency:o}),r){s.push({from:r,to:n});let a=i.get(r);a&&!a.children.includes(n)&&a.children.push(n)}if(e.dependencies)for(let[a,c]of e.dependencies){let u=`${a}@${c.version}`;ie(c,u,t+1,n,i,s,o)}}function V(e,n){let t=[];for(let r of e.roots){let i=e.nodes.get(r);if(i)if(i.name===n)t.push([r]);else{let s=se(e,r,n,[r]);t.push(...s)}}return t}function se(e,n,t,r){let i=[],s=e.nodes.get(n);if(!s)return i;for(let o of s.children){let a=e.nodes.get(o);if(!a)continue;let c=[...r,o];if(a.name===t)i.push(c);else{let u=se(e,o,t,c);i.push(...u)}}return i}function E(e,n){return e.nodes.get(n)?.depth??-1}function U(e){return Array.from(e.nodes.values()).filter(n=>n.depth===0)}function _(e){return Array.from(e.nodes.values()).filter(n=>n.depth>0)}function T(e,n={format:"tree"}){switch(n.format){case"tree":return Ke(e,n);case"dot":return Qe(e,n);case"json":return We(e,n);default:throw new Error(`Unsupported format: ${n.format}`)}}function Ke(e,n){let t=[],r=n.maxDepth??1/0,i=n.showVersions??!0;for(let s of e.roots)ae(e,s,0,"",t,r,i,n.highlightVulnerabilities);return t.join(` `)}function ae(e,n,t,r,i,s,o,a){if(t>s)return;let c=e.nodes.get(n);if(!c)return;let u=o?`${c.name}@${c.version}`:c.name,l=a&&c.vulnerabilities>0?` [${c.vulnerabilities} vuln(s)]`:"",f=c.isDevDependency?" (dev)":"";i.push(`${r}${u}${l}${f}`);let p=c.children;for(let d=0;d<p.length;d++){let g=d===p.length-1,m=r+(g?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 "),j=p[d];ae(e,j,t+1,m,i,s,o,a)}}function Qe(e,n){let t=[];t.push("digraph dependencies {"),t.push(" rankdir=LR;"),t.push(" node [shape=box];");for(let[r,i]of e.nodes){let s=n.showVersions?`${i.name}@${i.version}`:i.name,o=i.vulnerabilities>0&&n.highlightVulnerabilities?"red":"black",a=i.isDevDependency?"dashed":"solid";t.push(` "${r}" [label="${s}", color="${o}", style="${a}"];`)}for(let r of e.edges)t.push(` "${r.from}" -> "${r.to}";`);return t.push("}"),t.join(` `)}function We(e,n){let t={nodes:Array.from(e.nodes.values()),edges:e.edges,roots:e.roots};return JSON.stringify(t,null,2)}function L(e){let n=e.nodes.size,t=Array.from(e.nodes.values()).filter(a=>a.depth===0).length,r=n-t,i=Array.from(e.nodes.values()).filter(a=>a.isDevDependency).length,s=Array.from(e.nodes.values()).filter(a=>a.vulnerabilities>0).length;return["Dependency Graph Summary","========================",`Total packages: ${n}`,`Direct dependencies: ${t}`,`Transitive dependencies: ${r}`,`Dev dependencies: ${i}`,`Packages with vulnerabilities: ${s}`].join(` `)}var R=class{options;constructor(n){this.options={autoFix:!1,testBeforeUpdate:!0,createBackup:!0,...n}}async scan(n){return w(this.options.projectPath,{projectPath:this.options.projectPath,...n})}async analyzeBreakingChanges(n,t,r){return $(n,t,r)}async generateMigrations(n,t){return D(this.options.projectPath,n,t.changes)}async fix(n){let t=await this.scan(),r=n?.maxSeverity||"critical",i=n?.dryRun||!1,s=["low","moderate","high","critical"],o=s.indexOf(r),a=t.vulnerabilities.filter(l=>s.indexOf(l.severity)>=o),c=new Map;for(let l of a)c.has(l.package)||c.set(l.package,{current:l.version,patched:l.patched});let u=[];for(let[l,f]of c){let p=f.patched[0];if(!p){u.push({package:l,success:!1,reason:"No patched version available"});continue}let d=await this.analyzeBreakingChanges(l,f.current,p);if(d.hasBreakingChanges&&n?.breakingChanges==="skip"){u.push({package:l,success:!1,reason:"Breaking changes detected (skipped by policy)",breakingChanges:d});continue}let g=await this.generateMigrations(l,d);if(i){u.push({package:l,success:!0,dryRun:!0,fromVersion:f.current,toVersion:p,migrations:g,breakingChanges:d.hasBreakingChanges?d:void 0});continue}let m=await C(this.options.projectPath,l,p,g,{autoApply:this.options.autoFix,runTests:this.options.testBeforeUpdate,createBackup:this.options.createBackup});u.push({package:l,success:m.success,reason:m.reason,fromVersion:f.current,toVersion:p,migrations:m.migrations,failedTests:m.failedTests,rolledBack:m.rolledBack})}return{totalVulnerabilities:t.vulnerabilities.length,vulnerabilitiesFixed:a.length,packagesUpdated:u.filter(l=>l.success).length,packagesFailed:u.filter(l=>!l.success).length,results:u}}async findAlternatives(n,t){return M(n,t)}async visualizeDependencies(n){let t=await this.scan(),r=b(t.dependencies);return T(r,n)}async getDependencyGraph(){let n=await this.scan();return b(n.dependencies)}};0&&(module.exports={SecureSync,analyzeBreakingChanges,analyzeVersionDiff,buildGraph,findAlternatives,findDependencyPath,generateMigration,getDepth,getDirectDependencies,getTransitiveDependencies,parseChangelog,printSummary,runTests,scanNpmProject,scoreAlternative,testDrivenUpdate,visualize}); //# sourceMappingURL=index.js.map