@browsertotal/scanner 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';var f=require('puppeteer-core'),fs=require('fs'),child_process=require('child_process');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var f__default=/*#__PURE__*/_interopDefault(f);var P=Object.defineProperty;var S=(n,e)=>()=>(n&&(e=n(n=0)),e);var d=(n,e)=>{for(var s in e)P(n,s,{get:e[s],enumerable:true});};var p={};d(p,{BrowserTotalScanner:()=>exports.BrowserTotalScanner});function x(){let n=process.platform,e=w[n]||w.linux;for(let s of e)if(s&&fs.existsSync(s))return s;if(n!=="win32")try{let s=child_process.execSync("which google-chrome || which chromium || which chromium-browser",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();if(s&&fs.existsSync(s))return s}catch{}return null}function R(n){return Array.from(n).map(e=>e.charCodeAt(0).toString(16).padStart(2,"0")).join("")}var c,v,w,y;exports.BrowserTotalScanner=void 0;var u=S(()=>{c=process.env.BROWSERTOTAL_URL||"https://browsertotal.com",v=42e4,w={darwin:["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome","/Applications/Chromium.app/Contents/MacOS/Chromium","/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge","/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"],win32:["C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",process.env.LOCALAPPDATA+"\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe","C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe","C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe"],linux:["/usr/bin/google-chrome","/usr/bin/google-chrome-stable","/usr/bin/chromium","/usr/bin/chromium-browser","/snap/bin/chromium","/usr/bin/microsoft-edge","/usr/bin/brave-browser"]};y={chrome:"google",firefox:"mozilla",edge:"microsoft",opera:"opera",safari:"safari",brave:"brave"};exports.BrowserTotalScanner=class{options;browser=null;executablePath;constructor(e={}){if(this.options={headless:e.headless??true,timeout:e.timeout??v,waitForResults:e.waitForResults??true,disableAI:e.disableAI??true,userDataDir:e.userDataDir,executablePath:e.executablePath},this.executablePath=e.executablePath||x()||"",!this.executablePath)throw new Error("Chrome/Chromium not found. Please install Chrome or provide executablePath option.")}buildHashParams(){let e=["automationEvent=true"];return this.options.disableAI&&e.push("disableAI=true"),"#"+e.join("&")}async ensureBrowser(){return this.browser||(this.browser=await f__default.default.launch({executablePath:this.executablePath,headless:this.options.headless,args:["--no-sandbox","--disable-setuid-sandbox"],userDataDir:this.options.userDataDir})),this.browser}reportProgress(e,s){e&&e(s);}async scanUrl(e,s){let t=await(await this.ensureBrowser()).newPage();try{this.reportProgress(s,{phase:"initializing",message:"Starting URL scan..."});let a=R(e),l=`${c}/analysis/urls/${a}${this.buildHashParams()}`;this.reportProgress(s,{phase:"navigating",message:`Navigating to ${l}`});let o=this.waitForScanResultEvent(t,"url");if(await t.goto(l,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(s,{phase:"scanning",message:"Waiting for scan results..."}),this.options.waitForResults){let i=await o;if(i)return this.reportProgress(s,{phase:"complete",message:"Scan complete"}),this.mapUrlEventResult(i,e,l)}throw this.reportProgress(s,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await t.close();}}async scanExtension(e,s="chrome",r){let t=y[s]||s,a=`${c}/analysis/live/store/${t}/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,a,`${s} extension`,r)}async scanVSCodeExtension(e,s){let r=`${c}/analysis/live/store/vscode/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"VS Code extension",s)}async scanOpenVSXExtension(e,s){let r=`${c}/analysis/live/store/openvsx/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Open VSX extension",s)}async scanJetBrainsPlugin(e,s){let r=`${c}/analysis/live/store/jetbrains/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"JetBrains plugin",s)}async scanNpmPackage(e,s){let r=`${c}/analysis/live/store/npmjs/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"npmjs",r,"npm package",s)}async scanPyPIPackage(e,s){let r=`${c}/analysis/live/store/pypi/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"pypi",r,"PyPI package",s)}async scanWordPressPlugin(e,s){let r=`${c}/analysis/live/store/wordpress/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"WordPress plugin",s)}async scanHuggingFace(e,s){let r=`${c}/analysis/live/store/huggingface/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Hugging Face model",s)}async scanAppSourceAddin(e,s){let r=`${c}/analysis/live/store/appsource/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"AppSource add-in",s)}async scanPowerShellModule(e,s){let r=`${c}/analysis/live/store/powershellgallery/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"powershellgallery",r,"PowerShell module",s)}async scanSalesforceApp(e,s){let r=`${c}/analysis/live/store/salesforce/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Salesforce app",s)}async scanByPlatform(e,s,r){if(s in y)return this.scanExtension(e,s,r);switch(s){case "vscode":return this.scanVSCodeExtension(e,r);case "openvsx":return this.scanOpenVSXExtension(e,r);case "jetbrains":return this.scanJetBrainsPlugin(e,r);case "npmjs":return this.scanNpmPackage(e,r);case "pypi":return this.scanPyPIPackage(e,r);case "wordpress":return this.scanWordPressPlugin(e,r);case "huggingface":return this.scanHuggingFace(e,r);case "appsource":return this.scanAppSourceAddin(e,r);case "powershellgallery":return this.scanPowerShellModule(e,r);default:throw new Error(`Unsupported platform: ${s}`)}}async scanGenericExtension(e,s,r,t){let l=await(await this.ensureBrowser()).newPage();try{this.reportProgress(t,{phase:"initializing",message:`Starting ${r} scan...`}),this.reportProgress(t,{phase:"navigating",message:`Navigating to ${s}`});let o=this.waitForScanResultEvent(l,"extension");if(await l.goto(s,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(t,{phase:"scanning",message:`Waiting for ${r} analysis...`}),this.options.waitForResults){let i=await o;if(i)return this.reportProgress(t,{phase:"complete",message:"Scan complete"}),this.mapExtensionEventResult(i,e,s)}throw this.reportProgress(t,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await l.close();}}async scanGenericPackage(e,s,r,t,a){let o=await(await this.ensureBrowser()).newPage();try{this.reportProgress(a,{phase:"initializing",message:`Starting ${t} scan...`}),this.reportProgress(a,{phase:"navigating",message:`Navigating to ${r}`});let i=this.waitForScanResultEvent(o,"extension");if(await o.goto(r,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(a,{phase:"scanning",message:`Waiting for ${t} analysis...`}),this.options.waitForResults){let m=await i;if(m)return this.reportProgress(a,{phase:"complete",message:"Scan complete"}),this.mapPackageEventResult(m,e,s,r)}throw this.reportProgress(a,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await o.close();}}async waitForScanResultEvent(e,s){return new Promise(r=>{let t=setTimeout(()=>{console.log("[Scanner] Timeout waiting for scan_result event"),r(null);},this.options.timeout);e.exposeFunction("__browsertotalScanResult",a=>{clearTimeout(t),a?.type===s?(console.log("[Scanner] Received scan_result event:",a.type),r(a)):(console.log("[Scanner] Received wrong event type:",a?.type,"expected:",s),r(null));}).catch(()=>{}),e.evaluateOnNewDocument(`
1
+ 'use strict';var k=require('puppeteer-core'),fs=require('fs'),child_process=require('child_process');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var k__default=/*#__PURE__*/_interopDefault(k);var v=Object.defineProperty;var R=(n,e)=>()=>(n&&(e=n(n=0)),e);var x=(n,e)=>{for(var s in e)v(n,s,{get:e[s],enumerable:true});};var m={};x(m,{BrowserTotalScanner:()=>exports.BrowserTotalScanner});function A(){let n=process.platform,e=d[n]||d.linux;for(let s of e)if(s&&fs.existsSync(s))return s;if(n!=="win32")try{let s=child_process.execSync("which google-chrome || which chromium || which chromium-browser",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();if(s&&fs.existsSync(s))return s}catch{}return null}function B(n){return Array.from(n).map(e=>e.charCodeAt(0).toString(16).padStart(2,"0")).join("")}var l,C,$,d,f;exports.BrowserTotalScanner=void 0;var p=R(()=>{l=process.env.BROWSERTOTAL_URL||"https://browsertotal.com",C="app.browsertotal.com",$=42e4,d={darwin:["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome","/Applications/Chromium.app/Contents/MacOS/Chromium","/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge","/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"],win32:["C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",process.env.LOCALAPPDATA+"\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe","C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe","C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe"],linux:["/usr/bin/google-chrome","/usr/bin/google-chrome-stable","/usr/bin/chromium","/usr/bin/chromium-browser","/snap/bin/chromium","/usr/bin/microsoft-edge","/usr/bin/brave-browser"]};f={chrome:"google",firefox:"mozilla",edge:"microsoft",opera:"opera",safari:"safari",brave:"brave"};exports.BrowserTotalScanner=class{options;browser=null;executablePath;constructor(e={}){if(this.options={headless:e.headless??true,timeout:e.timeout??$,waitForResults:e.waitForResults??true,disableAI:e.disableAI??true,userDataDir:e.userDataDir,executablePath:e.executablePath},this.executablePath=e.executablePath||A()||"",!this.executablePath)throw new Error("Chrome/Chromium not found. Please install Chrome or provide executablePath option.")}buildHashParams(){let e=["automationEvent=true"];return this.options.disableAI&&e.push("disableAI=true"),"#"+e.join("&")}async setupCorsProxyBypass(e){await e.setRequestInterception(true),e.on("request",async s=>{let r=s.url();try{let t=new URL(r);if(t.host===C&&t.searchParams.has("t")){let a=t.searchParams.get("t"),c=decodeURIComponent(Buffer.from(a,"base64").toString("utf-8")),i={},o=s.headers(),h=["accept","accept-language","user-agent","content-type","authorization"];for(let u of h)o[u]&&(i[u]=o[u]);try{let u=await fetch(c,{method:s.method(),headers:i,body:s.method()!=="GET"&&s.method()!=="HEAD"?s.postData():void 0}),S=await u.arrayBuffer(),w={};u.headers.forEach((b,y)=>{["content-encoding","transfer-encoding","connection"].includes(y.toLowerCase())||(w[y]=b);}),w["access-control-allow-origin"]="*",await s.respond({status:u.status,headers:w,body:Buffer.from(S)});}catch(u){console.error(`[Scanner] CORS proxy bypass fetch error for ${c}:`,u),await s.continue();}return}}catch{}await s.continue();});}async ensureBrowser(){return this.browser||(this.browser=await k__default.default.launch({executablePath:this.executablePath,headless:this.options.headless,args:["--no-sandbox","--disable-setuid-sandbox"],userDataDir:this.options.userDataDir})),this.browser}async createPage(){let s=await(await this.ensureBrowser()).newPage();return await this.setupCorsProxyBypass(s),s}reportProgress(e,s){e&&e(s);}async scanUrl(e,s){let r=await this.createPage();try{this.reportProgress(s,{phase:"initializing",message:"Starting URL scan..."});let t=B(e),a=`${l}/analysis/urls/${t}${this.buildHashParams()}`;this.reportProgress(s,{phase:"navigating",message:`Navigating to ${a}`});let c=this.waitForScanResultEvent(r,"url");if(await r.goto(a,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(s,{phase:"scanning",message:"Waiting for scan results..."}),this.options.waitForResults){let i=await c;if(i)return this.reportProgress(s,{phase:"complete",message:"Scan complete"}),this.mapUrlEventResult(i,e,a)}throw this.reportProgress(s,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await r.close();}}async scanExtension(e,s="chrome",r){let t=f[s]||s,a=`${l}/analysis/live/store/${t}/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,a,`${s} extension`,r)}async scanVSCodeExtension(e,s){let r=`${l}/analysis/live/store/vscode/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"VS Code extension",s)}async scanOpenVSXExtension(e,s){let r=`${l}/analysis/live/store/openvsx/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Open VSX extension",s)}async scanJetBrainsPlugin(e,s){let r=`${l}/analysis/live/store/jetbrains/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"JetBrains plugin",s)}async scanNpmPackage(e,s){let r=`${l}/analysis/live/store/npmjs/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"npmjs",r,"npm package",s)}async scanPyPIPackage(e,s){let r=`${l}/analysis/live/store/pypi/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"pypi",r,"PyPI package",s)}async scanWordPressPlugin(e,s){let r=`${l}/analysis/live/store/wordpress/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"WordPress plugin",s)}async scanHuggingFace(e,s){let r=`${l}/analysis/live/store/huggingface/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Hugging Face model",s)}async scanAppSourceAddin(e,s){let r=`${l}/analysis/live/store/appsource/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"AppSource add-in",s)}async scanPowerShellModule(e,s){let r=`${l}/analysis/live/store/powershellgallery/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"powershellgallery",r,"PowerShell module",s)}async scanSalesforceApp(e,s){let r=`${l}/analysis/live/store/salesforce/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Salesforce app",s)}async scanByPlatform(e,s,r){if(s in f)return this.scanExtension(e,s,r);switch(s){case "vscode":return this.scanVSCodeExtension(e,r);case "openvsx":return this.scanOpenVSXExtension(e,r);case "jetbrains":return this.scanJetBrainsPlugin(e,r);case "npmjs":return this.scanNpmPackage(e,r);case "pypi":return this.scanPyPIPackage(e,r);case "wordpress":return this.scanWordPressPlugin(e,r);case "huggingface":return this.scanHuggingFace(e,r);case "appsource":return this.scanAppSourceAddin(e,r);case "powershellgallery":return this.scanPowerShellModule(e,r);default:throw new Error(`Unsupported platform: ${s}`)}}async scanGenericExtension(e,s,r,t){let a=await this.createPage();try{this.reportProgress(t,{phase:"initializing",message:`Starting ${r} scan...`}),this.reportProgress(t,{phase:"navigating",message:`Navigating to ${s}`});let c=this.waitForScanResultEvent(a,"extension");if(await a.goto(s,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(t,{phase:"scanning",message:`Waiting for ${r} analysis...`}),this.options.waitForResults){let i=await c;if(i)return this.reportProgress(t,{phase:"complete",message:"Scan complete"}),this.mapExtensionEventResult(i,e,s)}throw this.reportProgress(t,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await a.close();}}async scanGenericPackage(e,s,r,t,a){let c=await this.createPage();try{this.reportProgress(a,{phase:"initializing",message:`Starting ${t} scan...`}),this.reportProgress(a,{phase:"navigating",message:`Navigating to ${r}`});let i=this.waitForScanResultEvent(c,"extension");if(await c.goto(r,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(a,{phase:"scanning",message:`Waiting for ${t} analysis...`}),this.options.waitForResults){let o=await i;if(o)return this.reportProgress(a,{phase:"complete",message:"Scan complete"}),this.mapPackageEventResult(o,e,s,r)}throw this.reportProgress(a,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await c.close();}}async waitForScanResultEvent(e,s){return new Promise(r=>{let t=setTimeout(()=>{console.log("[Scanner] Timeout waiting for scan_result event"),r(null);},this.options.timeout);e.exposeFunction("__browsertotalScanResult",a=>{clearTimeout(t),a?.type===s?(console.log("[Scanner] Received scan_result event:",a.type),r(a)):(console.log("[Scanner] Received wrong event type:",a?.type,"expected:",s),r(null));}).catch(()=>{}),e.evaluateOnNewDocument(`
2
2
  window.addEventListener('scan_result', function(event) {
3
3
  console.log('[BrowserTotal] scan_result event fired');
4
4
  if (typeof window.__browsertotalScanResult === 'function') {
@@ -12,4 +12,4 @@
12
12
  window.__browsertotalScanResult(event.detail);
13
13
  }
14
14
  });
15
- `).catch(()=>{});});})}mapUrlEventResult(e,s,r){let t=e.data||{},o=((t.raw||{}).analysisResult||{}).riskAnalysis||{},i=o.overallRisk??t.score??t.riskScore??e.score;return {url:s,status:this.mapStatusFromScore(e.status,i,t.riskLevel),score:i,threats:this.mapThreats(t,o),categories:t.categories,scanUrl:r.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapExtensionEventResult(e,s,r){let t=e.data||{},o=((t.raw||{}).analysisResult||{}).riskAnalysis||{},i=o.overallRisk??t.score??t.riskScore??e.score;return {extensionId:s,name:t.name||t.metadata?.title,status:this.mapStatusFromScore(e.status,i,t.riskLevel),score:i,permissions:t.permissions,threats:this.mapThreats(t,o),scanUrl:r.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapPackageEventResult(e,s,r,t){let a=e.data||{},i=((a.raw||{}).analysisResult||{}).riskAnalysis||{},m=i.overallRisk??a.score??a.riskScore??e.score;return {packageName:s,platform:r,name:a.name||a.metadata?.title,version:a.version,status:this.mapStatusFromScore(e.status,m,a.riskLevel),score:m,dependencies:a.dependencies,threats:this.mapThreats(a,i),scanUrl:t.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapThreats(e,s){let r=s.risks;if(Array.isArray(r)&&r.length>0)return r.map(t=>({type:this.extractThreatType(t.description),severity:this.mapPointsToSeverity(t.points),description:t.description}));if(Array.isArray(e.threats)&&e.threats.length>0)return e.threats.map(t=>({type:typeof t=="string"?t:t.type||t.description,severity:t.severity||"medium",description:t.description}));if(Array.isArray(e.vulnerabilities)&&e.vulnerabilities.length>0)return e.vulnerabilities.flatMap(t=>Array.isArray(t.vulnerabilities)?t.vulnerabilities.map(a=>({type:`${t.component}@${t.version}`,severity:a.severity||"medium",description:a.identifiers?.summary||a.description||`Vulnerability in ${t.component}`})):[{type:t.type||t.component||"vulnerability",severity:t.severity||"medium",description:t.description||t.identifiers?.summary}])}extractThreatType(e){return e?e.includes("vulnerabilities")?"dependency-vulnerability":e.includes("obfuscated")?"obfuscation":e.includes("install count")?"low-adoption":e.includes("Workspace Trust")?"workspace-trust":e.includes("codebase")?"attack-surface":e.includes("Publisher")?"publisher-risk":e.includes("permission")?"permission-risk":"risk-indicator":"unknown"}mapPointsToSeverity(e){return e>=20?"critical":e>=15?"high":e>=10?"medium":"low"}mapStatusFromScore(e,s,r){if(e==="error")return "error";let t=(r||"").toLowerCase();if(t==="malicious")return "malicious";if(t==="critical")return "critical";if(t==="high"||t==="suspicious"||t==="medium")return "suspicious";if(t==="safe"||t==="low"||t==="clean")return "safe";if(typeof s=="number"){if(s>=50)return "suspicious";if(s<50)return "safe"}return "unknown"}async close(){this.browser&&(await this.browser.close(),this.browser=null);}async[Symbol.asyncDispose](){await this.close();}};});u();async function U(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanUrl(n)}finally{await r.close();}}async function T(n,e="chrome",s){let{BrowserTotalScanner:r}=await Promise.resolve().then(()=>(u(),p)),t=new r(s);try{return await t.scanExtension(n,e)}finally{await t.close();}}async function O(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanVSCodeExtension(n)}finally{await r.close();}}async function F(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanJetBrainsPlugin(n)}finally{await r.close();}}async function _(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanNpmPackage(n)}finally{await r.close();}}async function H(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanPyPIPackage(n)}finally{await r.close();}}async function D(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanWordPressPlugin(n)}finally{await r.close();}}exports.scanExtension=T;exports.scanJetBrainsPlugin=F;exports.scanNpmPackage=_;exports.scanPyPIPackage=H;exports.scanUrl=U;exports.scanVSCodeExtension=O;exports.scanWordPressPlugin=D;
15
+ `).catch(()=>{});});})}mapUrlEventResult(e,s,r){let t=e.data||{},i=((t.raw||{}).analysisResult||{}).riskAnalysis||{},o=i.overallRisk??t.score??t.riskScore??e.score;return {url:s,status:this.mapStatusFromScore(e.status,o,t.riskLevel),score:o,threats:this.mapThreats(t,i),categories:t.categories,scanUrl:r.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapExtensionEventResult(e,s,r){let t=e.data||{},i=((t.raw||{}).analysisResult||{}).riskAnalysis||{},o=i.overallRisk??t.score??t.riskScore??e.score;return {extensionId:s,name:t.name||t.metadata?.title,status:this.mapStatusFromScore(e.status,o,t.riskLevel),score:o,permissions:t.permissions,threats:this.mapThreats(t,i),scanUrl:r.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapPackageEventResult(e,s,r,t){let a=e.data||{},o=((a.raw||{}).analysisResult||{}).riskAnalysis||{},h=o.overallRisk??a.score??a.riskScore??e.score;return {packageName:s,platform:r,name:a.name||a.metadata?.title,version:a.version,status:this.mapStatusFromScore(e.status,h,a.riskLevel),score:h,dependencies:a.dependencies,threats:this.mapThreats(a,o),scanUrl:t.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapThreats(e,s){let r=s.risks;if(Array.isArray(r)&&r.length>0)return r.map(t=>({type:this.extractThreatType(t.description),severity:this.mapPointsToSeverity(t.points),description:t.description}));if(Array.isArray(e.threats)&&e.threats.length>0)return e.threats.map(t=>({type:typeof t=="string"?t:t.type||t.description,severity:t.severity||"medium",description:t.description}));if(Array.isArray(e.vulnerabilities)&&e.vulnerabilities.length>0)return e.vulnerabilities.flatMap(t=>Array.isArray(t.vulnerabilities)?t.vulnerabilities.map(a=>({type:`${t.component}@${t.version}`,severity:a.severity||"medium",description:a.identifiers?.summary||a.description||`Vulnerability in ${t.component}`})):[{type:t.type||t.component||"vulnerability",severity:t.severity||"medium",description:t.description||t.identifiers?.summary}])}extractThreatType(e){return e?e.includes("vulnerabilities")?"dependency-vulnerability":e.includes("obfuscated")?"obfuscation":e.includes("install count")?"low-adoption":e.includes("Workspace Trust")?"workspace-trust":e.includes("codebase")?"attack-surface":e.includes("Publisher")?"publisher-risk":e.includes("permission")?"permission-risk":"risk-indicator":"unknown"}mapPointsToSeverity(e){return e>=20?"critical":e>=15?"high":e>=10?"medium":"low"}mapStatusFromScore(e,s,r){if(e==="error")return "error";let t=(r||"").toLowerCase();return t==="critical"?"critical":t==="high"?"high":t==="medium"?"medium":t==="low"||t==="safe"?"low":typeof s=="number"?s>=90?"critical":s>=60?"high":s>=40?"medium":"low":"unknown"}async close(){this.browser&&(await this.browser.close(),this.browser=null);}async[Symbol.asyncDispose](){await this.close();}};});p();async function G(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanUrl(n)}finally{await r.close();}}async function L(n,e="chrome",s){let{BrowserTotalScanner:r}=await Promise.resolve().then(()=>(p(),m)),t=new r(s);try{return await t.scanExtension(n,e)}finally{await t.close();}}async function M(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanVSCodeExtension(n)}finally{await r.close();}}async function j(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanJetBrainsPlugin(n)}finally{await r.close();}}async function I(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanNpmPackage(n)}finally{await r.close();}}async function W(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanPyPIPackage(n)}finally{await r.close();}}async function V(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanWordPressPlugin(n)}finally{await r.close();}}exports.scanExtension=L;exports.scanJetBrainsPlugin=j;exports.scanNpmPackage=I;exports.scanPyPIPackage=W;exports.scanUrl=G;exports.scanVSCodeExtension=M;exports.scanWordPressPlugin=V;
package/dist/index.d.cts CHANGED
@@ -13,9 +13,10 @@ interface ThreatInfo {
13
13
  severity: 'low' | 'medium' | 'high' | 'critical';
14
14
  description?: string;
15
15
  }
16
+ type RiskStatus = 'critical' | 'high' | 'medium' | 'low' | 'unknown' | 'error';
16
17
  interface UrlScanResult {
17
18
  url: string;
18
- status: 'safe' | 'suspicious' | 'malicious' | 'critical' | 'unknown' | 'error';
19
+ status: RiskStatus;
19
20
  score?: number;
20
21
  threats?: ThreatInfo[];
21
22
  categories?: string[];
@@ -27,7 +28,7 @@ interface UrlScanResult {
27
28
  interface ExtensionScanResult {
28
29
  extensionId: string;
29
30
  name?: string;
30
- status: 'safe' | 'suspicious' | 'malicious' | 'critical' | 'unknown' | 'error';
31
+ status: RiskStatus;
31
32
  score?: number;
32
33
  permissions?: string[];
33
34
  threats?: ThreatInfo[];
@@ -41,7 +42,7 @@ interface PackageScanResult {
41
42
  platform: string;
42
43
  name?: string;
43
44
  version?: string;
44
- status: 'safe' | 'suspicious' | 'malicious' | 'critical' | 'unknown' | 'error';
45
+ status: RiskStatus;
45
46
  score?: number;
46
47
  dependencies?: Record<string, string>;
47
48
  threats?: ThreatInfo[];
@@ -62,7 +63,9 @@ declare class BrowserTotalScanner {
62
63
  private executablePath;
63
64
  constructor(options?: ScannerOptions);
64
65
  private buildHashParams;
66
+ private setupCorsProxyBypass;
65
67
  private ensureBrowser;
68
+ private createPage;
66
69
  private reportProgress;
67
70
  scanUrl(url: string, onProgress?: ProgressCallback): Promise<UrlScanResult>;
68
71
  scanExtension(extensionId: string, store?: BrowserStore, onProgress?: ProgressCallback): Promise<ExtensionScanResult>;
@@ -99,4 +102,4 @@ declare function scanNpmPackage(packageName: string, options?: ScannerOptions):
99
102
  declare function scanPyPIPackage(packageName: string, options?: ScannerOptions): Promise<PackageScanResult>;
100
103
  declare function scanWordPressPlugin(pluginSlug: string, options?: ScannerOptions): Promise<ExtensionScanResult>;
101
104
 
102
- export { type BrowserStore, BrowserTotalScanner, type ExtensionScanResult, type PackageScanResult, type Platform, type ProgressCallback, type ScanProgress, type ScannerOptions, type ThreatInfo, type UrlScanResult, scanExtension, scanJetBrainsPlugin, scanNpmPackage, scanPyPIPackage, scanUrl, scanVSCodeExtension, scanWordPressPlugin };
105
+ export { type BrowserStore, BrowserTotalScanner, type ExtensionScanResult, type PackageScanResult, type Platform, type ProgressCallback, type RiskStatus, type ScanProgress, type ScannerOptions, type ThreatInfo, type UrlScanResult, scanExtension, scanJetBrainsPlugin, scanNpmPackage, scanPyPIPackage, scanUrl, scanVSCodeExtension, scanWordPressPlugin };
package/dist/index.d.ts CHANGED
@@ -13,9 +13,10 @@ interface ThreatInfo {
13
13
  severity: 'low' | 'medium' | 'high' | 'critical';
14
14
  description?: string;
15
15
  }
16
+ type RiskStatus = 'critical' | 'high' | 'medium' | 'low' | 'unknown' | 'error';
16
17
  interface UrlScanResult {
17
18
  url: string;
18
- status: 'safe' | 'suspicious' | 'malicious' | 'critical' | 'unknown' | 'error';
19
+ status: RiskStatus;
19
20
  score?: number;
20
21
  threats?: ThreatInfo[];
21
22
  categories?: string[];
@@ -27,7 +28,7 @@ interface UrlScanResult {
27
28
  interface ExtensionScanResult {
28
29
  extensionId: string;
29
30
  name?: string;
30
- status: 'safe' | 'suspicious' | 'malicious' | 'critical' | 'unknown' | 'error';
31
+ status: RiskStatus;
31
32
  score?: number;
32
33
  permissions?: string[];
33
34
  threats?: ThreatInfo[];
@@ -41,7 +42,7 @@ interface PackageScanResult {
41
42
  platform: string;
42
43
  name?: string;
43
44
  version?: string;
44
- status: 'safe' | 'suspicious' | 'malicious' | 'critical' | 'unknown' | 'error';
45
+ status: RiskStatus;
45
46
  score?: number;
46
47
  dependencies?: Record<string, string>;
47
48
  threats?: ThreatInfo[];
@@ -62,7 +63,9 @@ declare class BrowserTotalScanner {
62
63
  private executablePath;
63
64
  constructor(options?: ScannerOptions);
64
65
  private buildHashParams;
66
+ private setupCorsProxyBypass;
65
67
  private ensureBrowser;
68
+ private createPage;
66
69
  private reportProgress;
67
70
  scanUrl(url: string, onProgress?: ProgressCallback): Promise<UrlScanResult>;
68
71
  scanExtension(extensionId: string, store?: BrowserStore, onProgress?: ProgressCallback): Promise<ExtensionScanResult>;
@@ -99,4 +102,4 @@ declare function scanNpmPackage(packageName: string, options?: ScannerOptions):
99
102
  declare function scanPyPIPackage(packageName: string, options?: ScannerOptions): Promise<PackageScanResult>;
100
103
  declare function scanWordPressPlugin(pluginSlug: string, options?: ScannerOptions): Promise<ExtensionScanResult>;
101
104
 
102
- export { type BrowserStore, BrowserTotalScanner, type ExtensionScanResult, type PackageScanResult, type Platform, type ProgressCallback, type ScanProgress, type ScannerOptions, type ThreatInfo, type UrlScanResult, scanExtension, scanJetBrainsPlugin, scanNpmPackage, scanPyPIPackage, scanUrl, scanVSCodeExtension, scanWordPressPlugin };
105
+ export { type BrowserStore, BrowserTotalScanner, type ExtensionScanResult, type PackageScanResult, type Platform, type ProgressCallback, type RiskStatus, type ScanProgress, type ScannerOptions, type ThreatInfo, type UrlScanResult, scanExtension, scanJetBrainsPlugin, scanNpmPackage, scanPyPIPackage, scanUrl, scanVSCodeExtension, scanWordPressPlugin };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import f from'puppeteer-core';import {existsSync}from'fs';import {execSync}from'child_process';var P=Object.defineProperty;var S=(n,e)=>()=>(n&&(e=n(n=0)),e);var d=(n,e)=>{for(var s in e)P(n,s,{get:e[s],enumerable:true});};var p={};d(p,{BrowserTotalScanner:()=>g});function x(){let n=process.platform,e=w[n]||w.linux;for(let s of e)if(s&&existsSync(s))return s;if(n!=="win32")try{let s=execSync("which google-chrome || which chromium || which chromium-browser",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();if(s&&existsSync(s))return s}catch{}return null}function R(n){return Array.from(n).map(e=>e.charCodeAt(0).toString(16).padStart(2,"0")).join("")}var c,v,w,y,g,u=S(()=>{c=process.env.BROWSERTOTAL_URL||"https://browsertotal.com",v=42e4,w={darwin:["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome","/Applications/Chromium.app/Contents/MacOS/Chromium","/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge","/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"],win32:["C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",process.env.LOCALAPPDATA+"\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe","C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe","C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe"],linux:["/usr/bin/google-chrome","/usr/bin/google-chrome-stable","/usr/bin/chromium","/usr/bin/chromium-browser","/snap/bin/chromium","/usr/bin/microsoft-edge","/usr/bin/brave-browser"]};y={chrome:"google",firefox:"mozilla",edge:"microsoft",opera:"opera",safari:"safari",brave:"brave"};g=class{options;browser=null;executablePath;constructor(e={}){if(this.options={headless:e.headless??true,timeout:e.timeout??v,waitForResults:e.waitForResults??true,disableAI:e.disableAI??true,userDataDir:e.userDataDir,executablePath:e.executablePath},this.executablePath=e.executablePath||x()||"",!this.executablePath)throw new Error("Chrome/Chromium not found. Please install Chrome or provide executablePath option.")}buildHashParams(){let e=["automationEvent=true"];return this.options.disableAI&&e.push("disableAI=true"),"#"+e.join("&")}async ensureBrowser(){return this.browser||(this.browser=await f.launch({executablePath:this.executablePath,headless:this.options.headless,args:["--no-sandbox","--disable-setuid-sandbox"],userDataDir:this.options.userDataDir})),this.browser}reportProgress(e,s){e&&e(s);}async scanUrl(e,s){let t=await(await this.ensureBrowser()).newPage();try{this.reportProgress(s,{phase:"initializing",message:"Starting URL scan..."});let a=R(e),l=`${c}/analysis/urls/${a}${this.buildHashParams()}`;this.reportProgress(s,{phase:"navigating",message:`Navigating to ${l}`});let o=this.waitForScanResultEvent(t,"url");if(await t.goto(l,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(s,{phase:"scanning",message:"Waiting for scan results..."}),this.options.waitForResults){let i=await o;if(i)return this.reportProgress(s,{phase:"complete",message:"Scan complete"}),this.mapUrlEventResult(i,e,l)}throw this.reportProgress(s,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await t.close();}}async scanExtension(e,s="chrome",r){let t=y[s]||s,a=`${c}/analysis/live/store/${t}/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,a,`${s} extension`,r)}async scanVSCodeExtension(e,s){let r=`${c}/analysis/live/store/vscode/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"VS Code extension",s)}async scanOpenVSXExtension(e,s){let r=`${c}/analysis/live/store/openvsx/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Open VSX extension",s)}async scanJetBrainsPlugin(e,s){let r=`${c}/analysis/live/store/jetbrains/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"JetBrains plugin",s)}async scanNpmPackage(e,s){let r=`${c}/analysis/live/store/npmjs/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"npmjs",r,"npm package",s)}async scanPyPIPackage(e,s){let r=`${c}/analysis/live/store/pypi/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"pypi",r,"PyPI package",s)}async scanWordPressPlugin(e,s){let r=`${c}/analysis/live/store/wordpress/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"WordPress plugin",s)}async scanHuggingFace(e,s){let r=`${c}/analysis/live/store/huggingface/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Hugging Face model",s)}async scanAppSourceAddin(e,s){let r=`${c}/analysis/live/store/appsource/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"AppSource add-in",s)}async scanPowerShellModule(e,s){let r=`${c}/analysis/live/store/powershellgallery/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"powershellgallery",r,"PowerShell module",s)}async scanSalesforceApp(e,s){let r=`${c}/analysis/live/store/salesforce/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Salesforce app",s)}async scanByPlatform(e,s,r){if(s in y)return this.scanExtension(e,s,r);switch(s){case "vscode":return this.scanVSCodeExtension(e,r);case "openvsx":return this.scanOpenVSXExtension(e,r);case "jetbrains":return this.scanJetBrainsPlugin(e,r);case "npmjs":return this.scanNpmPackage(e,r);case "pypi":return this.scanPyPIPackage(e,r);case "wordpress":return this.scanWordPressPlugin(e,r);case "huggingface":return this.scanHuggingFace(e,r);case "appsource":return this.scanAppSourceAddin(e,r);case "powershellgallery":return this.scanPowerShellModule(e,r);default:throw new Error(`Unsupported platform: ${s}`)}}async scanGenericExtension(e,s,r,t){let l=await(await this.ensureBrowser()).newPage();try{this.reportProgress(t,{phase:"initializing",message:`Starting ${r} scan...`}),this.reportProgress(t,{phase:"navigating",message:`Navigating to ${s}`});let o=this.waitForScanResultEvent(l,"extension");if(await l.goto(s,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(t,{phase:"scanning",message:`Waiting for ${r} analysis...`}),this.options.waitForResults){let i=await o;if(i)return this.reportProgress(t,{phase:"complete",message:"Scan complete"}),this.mapExtensionEventResult(i,e,s)}throw this.reportProgress(t,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await l.close();}}async scanGenericPackage(e,s,r,t,a){let o=await(await this.ensureBrowser()).newPage();try{this.reportProgress(a,{phase:"initializing",message:`Starting ${t} scan...`}),this.reportProgress(a,{phase:"navigating",message:`Navigating to ${r}`});let i=this.waitForScanResultEvent(o,"extension");if(await o.goto(r,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(a,{phase:"scanning",message:`Waiting for ${t} analysis...`}),this.options.waitForResults){let m=await i;if(m)return this.reportProgress(a,{phase:"complete",message:"Scan complete"}),this.mapPackageEventResult(m,e,s,r)}throw this.reportProgress(a,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await o.close();}}async waitForScanResultEvent(e,s){return new Promise(r=>{let t=setTimeout(()=>{console.log("[Scanner] Timeout waiting for scan_result event"),r(null);},this.options.timeout);e.exposeFunction("__browsertotalScanResult",a=>{clearTimeout(t),a?.type===s?(console.log("[Scanner] Received scan_result event:",a.type),r(a)):(console.log("[Scanner] Received wrong event type:",a?.type,"expected:",s),r(null));}).catch(()=>{}),e.evaluateOnNewDocument(`
1
+ import k from'puppeteer-core';import {existsSync}from'fs';import {execSync}from'child_process';var v=Object.defineProperty;var R=(n,e)=>()=>(n&&(e=n(n=0)),e);var x=(n,e)=>{for(var s in e)v(n,s,{get:e[s],enumerable:true});};var m={};x(m,{BrowserTotalScanner:()=>g});function A(){let n=process.platform,e=d[n]||d.linux;for(let s of e)if(s&&existsSync(s))return s;if(n!=="win32")try{let s=execSync("which google-chrome || which chromium || which chromium-browser",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();if(s&&existsSync(s))return s}catch{}return null}function B(n){return Array.from(n).map(e=>e.charCodeAt(0).toString(16).padStart(2,"0")).join("")}var l,C,$,d,f,g,p=R(()=>{l=process.env.BROWSERTOTAL_URL||"https://browsertotal.com",C="app.browsertotal.com",$=42e4,d={darwin:["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome","/Applications/Chromium.app/Contents/MacOS/Chromium","/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge","/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"],win32:["C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",process.env.LOCALAPPDATA+"\\Google\\Chrome\\Application\\chrome.exe","C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe","C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe","C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe"],linux:["/usr/bin/google-chrome","/usr/bin/google-chrome-stable","/usr/bin/chromium","/usr/bin/chromium-browser","/snap/bin/chromium","/usr/bin/microsoft-edge","/usr/bin/brave-browser"]};f={chrome:"google",firefox:"mozilla",edge:"microsoft",opera:"opera",safari:"safari",brave:"brave"};g=class{options;browser=null;executablePath;constructor(e={}){if(this.options={headless:e.headless??true,timeout:e.timeout??$,waitForResults:e.waitForResults??true,disableAI:e.disableAI??true,userDataDir:e.userDataDir,executablePath:e.executablePath},this.executablePath=e.executablePath||A()||"",!this.executablePath)throw new Error("Chrome/Chromium not found. Please install Chrome or provide executablePath option.")}buildHashParams(){let e=["automationEvent=true"];return this.options.disableAI&&e.push("disableAI=true"),"#"+e.join("&")}async setupCorsProxyBypass(e){await e.setRequestInterception(true),e.on("request",async s=>{let r=s.url();try{let t=new URL(r);if(t.host===C&&t.searchParams.has("t")){let a=t.searchParams.get("t"),c=decodeURIComponent(Buffer.from(a,"base64").toString("utf-8")),i={},o=s.headers(),h=["accept","accept-language","user-agent","content-type","authorization"];for(let u of h)o[u]&&(i[u]=o[u]);try{let u=await fetch(c,{method:s.method(),headers:i,body:s.method()!=="GET"&&s.method()!=="HEAD"?s.postData():void 0}),S=await u.arrayBuffer(),w={};u.headers.forEach((b,y)=>{["content-encoding","transfer-encoding","connection"].includes(y.toLowerCase())||(w[y]=b);}),w["access-control-allow-origin"]="*",await s.respond({status:u.status,headers:w,body:Buffer.from(S)});}catch(u){console.error(`[Scanner] CORS proxy bypass fetch error for ${c}:`,u),await s.continue();}return}}catch{}await s.continue();});}async ensureBrowser(){return this.browser||(this.browser=await k.launch({executablePath:this.executablePath,headless:this.options.headless,args:["--no-sandbox","--disable-setuid-sandbox"],userDataDir:this.options.userDataDir})),this.browser}async createPage(){let s=await(await this.ensureBrowser()).newPage();return await this.setupCorsProxyBypass(s),s}reportProgress(e,s){e&&e(s);}async scanUrl(e,s){let r=await this.createPage();try{this.reportProgress(s,{phase:"initializing",message:"Starting URL scan..."});let t=B(e),a=`${l}/analysis/urls/${t}${this.buildHashParams()}`;this.reportProgress(s,{phase:"navigating",message:`Navigating to ${a}`});let c=this.waitForScanResultEvent(r,"url");if(await r.goto(a,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(s,{phase:"scanning",message:"Waiting for scan results..."}),this.options.waitForResults){let i=await c;if(i)return this.reportProgress(s,{phase:"complete",message:"Scan complete"}),this.mapUrlEventResult(i,e,a)}throw this.reportProgress(s,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await r.close();}}async scanExtension(e,s="chrome",r){let t=f[s]||s,a=`${l}/analysis/live/store/${t}/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,a,`${s} extension`,r)}async scanVSCodeExtension(e,s){let r=`${l}/analysis/live/store/vscode/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"VS Code extension",s)}async scanOpenVSXExtension(e,s){let r=`${l}/analysis/live/store/openvsx/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Open VSX extension",s)}async scanJetBrainsPlugin(e,s){let r=`${l}/analysis/live/store/jetbrains/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"JetBrains plugin",s)}async scanNpmPackage(e,s){let r=`${l}/analysis/live/store/npmjs/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"npmjs",r,"npm package",s)}async scanPyPIPackage(e,s){let r=`${l}/analysis/live/store/pypi/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"pypi",r,"PyPI package",s)}async scanWordPressPlugin(e,s){let r=`${l}/analysis/live/store/wordpress/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"WordPress plugin",s)}async scanHuggingFace(e,s){let r=`${l}/analysis/live/store/huggingface/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Hugging Face model",s)}async scanAppSourceAddin(e,s){let r=`${l}/analysis/live/store/appsource/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"AppSource add-in",s)}async scanPowerShellModule(e,s){let r=`${l}/analysis/live/store/powershellgallery/${encodeURIComponent(e)}${this.buildHashParams()}`;return this.scanGenericPackage(e,"powershellgallery",r,"PowerShell module",s)}async scanSalesforceApp(e,s){let r=`${l}/analysis/live/store/salesforce/${e}${this.buildHashParams()}`;return this.scanGenericExtension(e,r,"Salesforce app",s)}async scanByPlatform(e,s,r){if(s in f)return this.scanExtension(e,s,r);switch(s){case "vscode":return this.scanVSCodeExtension(e,r);case "openvsx":return this.scanOpenVSXExtension(e,r);case "jetbrains":return this.scanJetBrainsPlugin(e,r);case "npmjs":return this.scanNpmPackage(e,r);case "pypi":return this.scanPyPIPackage(e,r);case "wordpress":return this.scanWordPressPlugin(e,r);case "huggingface":return this.scanHuggingFace(e,r);case "appsource":return this.scanAppSourceAddin(e,r);case "powershellgallery":return this.scanPowerShellModule(e,r);default:throw new Error(`Unsupported platform: ${s}`)}}async scanGenericExtension(e,s,r,t){let a=await this.createPage();try{this.reportProgress(t,{phase:"initializing",message:`Starting ${r} scan...`}),this.reportProgress(t,{phase:"navigating",message:`Navigating to ${s}`});let c=this.waitForScanResultEvent(a,"extension");if(await a.goto(s,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(t,{phase:"scanning",message:`Waiting for ${r} analysis...`}),this.options.waitForResults){let i=await c;if(i)return this.reportProgress(t,{phase:"complete",message:"Scan complete"}),this.mapExtensionEventResult(i,e,s)}throw this.reportProgress(t,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await a.close();}}async scanGenericPackage(e,s,r,t,a){let c=await this.createPage();try{this.reportProgress(a,{phase:"initializing",message:`Starting ${t} scan...`}),this.reportProgress(a,{phase:"navigating",message:`Navigating to ${r}`});let i=this.waitForScanResultEvent(c,"extension");if(await c.goto(r,{waitUntil:"networkidle2",timeout:this.options.timeout}),this.reportProgress(a,{phase:"scanning",message:`Waiting for ${t} analysis...`}),this.options.waitForResults){let o=await i;if(o)return this.reportProgress(a,{phase:"complete",message:"Scan complete"}),this.mapPackageEventResult(o,e,s,r)}throw this.reportProgress(a,{phase:"complete",message:"Scan error"}),new Error("Scan error")}finally{await c.close();}}async waitForScanResultEvent(e,s){return new Promise(r=>{let t=setTimeout(()=>{console.log("[Scanner] Timeout waiting for scan_result event"),r(null);},this.options.timeout);e.exposeFunction("__browsertotalScanResult",a=>{clearTimeout(t),a?.type===s?(console.log("[Scanner] Received scan_result event:",a.type),r(a)):(console.log("[Scanner] Received wrong event type:",a?.type,"expected:",s),r(null));}).catch(()=>{}),e.evaluateOnNewDocument(`
2
2
  window.addEventListener('scan_result', function(event) {
3
3
  console.log('[BrowserTotal] scan_result event fired');
4
4
  if (typeof window.__browsertotalScanResult === 'function') {
@@ -12,4 +12,4 @@ import f from'puppeteer-core';import {existsSync}from'fs';import {execSync}from'
12
12
  window.__browsertotalScanResult(event.detail);
13
13
  }
14
14
  });
15
- `).catch(()=>{});});})}mapUrlEventResult(e,s,r){let t=e.data||{},o=((t.raw||{}).analysisResult||{}).riskAnalysis||{},i=o.overallRisk??t.score??t.riskScore??e.score;return {url:s,status:this.mapStatusFromScore(e.status,i,t.riskLevel),score:i,threats:this.mapThreats(t,o),categories:t.categories,scanUrl:r.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapExtensionEventResult(e,s,r){let t=e.data||{},o=((t.raw||{}).analysisResult||{}).riskAnalysis||{},i=o.overallRisk??t.score??t.riskScore??e.score;return {extensionId:s,name:t.name||t.metadata?.title,status:this.mapStatusFromScore(e.status,i,t.riskLevel),score:i,permissions:t.permissions,threats:this.mapThreats(t,o),scanUrl:r.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapPackageEventResult(e,s,r,t){let a=e.data||{},i=((a.raw||{}).analysisResult||{}).riskAnalysis||{},m=i.overallRisk??a.score??a.riskScore??e.score;return {packageName:s,platform:r,name:a.name||a.metadata?.title,version:a.version,status:this.mapStatusFromScore(e.status,m,a.riskLevel),score:m,dependencies:a.dependencies,threats:this.mapThreats(a,i),scanUrl:t.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapThreats(e,s){let r=s.risks;if(Array.isArray(r)&&r.length>0)return r.map(t=>({type:this.extractThreatType(t.description),severity:this.mapPointsToSeverity(t.points),description:t.description}));if(Array.isArray(e.threats)&&e.threats.length>0)return e.threats.map(t=>({type:typeof t=="string"?t:t.type||t.description,severity:t.severity||"medium",description:t.description}));if(Array.isArray(e.vulnerabilities)&&e.vulnerabilities.length>0)return e.vulnerabilities.flatMap(t=>Array.isArray(t.vulnerabilities)?t.vulnerabilities.map(a=>({type:`${t.component}@${t.version}`,severity:a.severity||"medium",description:a.identifiers?.summary||a.description||`Vulnerability in ${t.component}`})):[{type:t.type||t.component||"vulnerability",severity:t.severity||"medium",description:t.description||t.identifiers?.summary}])}extractThreatType(e){return e?e.includes("vulnerabilities")?"dependency-vulnerability":e.includes("obfuscated")?"obfuscation":e.includes("install count")?"low-adoption":e.includes("Workspace Trust")?"workspace-trust":e.includes("codebase")?"attack-surface":e.includes("Publisher")?"publisher-risk":e.includes("permission")?"permission-risk":"risk-indicator":"unknown"}mapPointsToSeverity(e){return e>=20?"critical":e>=15?"high":e>=10?"medium":"low"}mapStatusFromScore(e,s,r){if(e==="error")return "error";let t=(r||"").toLowerCase();if(t==="malicious")return "malicious";if(t==="critical")return "critical";if(t==="high"||t==="suspicious"||t==="medium")return "suspicious";if(t==="safe"||t==="low"||t==="clean")return "safe";if(typeof s=="number"){if(s>=50)return "suspicious";if(s<50)return "safe"}return "unknown"}async close(){this.browser&&(await this.browser.close(),this.browser=null);}async[Symbol.asyncDispose](){await this.close();}};});u();async function U(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanUrl(n)}finally{await r.close();}}async function T(n,e="chrome",s){let{BrowserTotalScanner:r}=await Promise.resolve().then(()=>(u(),p)),t=new r(s);try{return await t.scanExtension(n,e)}finally{await t.close();}}async function O(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanVSCodeExtension(n)}finally{await r.close();}}async function F(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanJetBrainsPlugin(n)}finally{await r.close();}}async function _(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanNpmPackage(n)}finally{await r.close();}}async function H(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanPyPIPackage(n)}finally{await r.close();}}async function D(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(u(),p)),r=new s(e);try{return await r.scanWordPressPlugin(n)}finally{await r.close();}}export{g as BrowserTotalScanner,T as scanExtension,F as scanJetBrainsPlugin,_ as scanNpmPackage,H as scanPyPIPackage,U as scanUrl,O as scanVSCodeExtension,D as scanWordPressPlugin};
15
+ `).catch(()=>{});});})}mapUrlEventResult(e,s,r){let t=e.data||{},i=((t.raw||{}).analysisResult||{}).riskAnalysis||{},o=i.overallRisk??t.score??t.riskScore??e.score;return {url:s,status:this.mapStatusFromScore(e.status,o,t.riskLevel),score:o,threats:this.mapThreats(t,i),categories:t.categories,scanUrl:r.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapExtensionEventResult(e,s,r){let t=e.data||{},i=((t.raw||{}).analysisResult||{}).riskAnalysis||{},o=i.overallRisk??t.score??t.riskScore??e.score;return {extensionId:s,name:t.name||t.metadata?.title,status:this.mapStatusFromScore(e.status,o,t.riskLevel),score:o,permissions:t.permissions,threats:this.mapThreats(t,i),scanUrl:r.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapPackageEventResult(e,s,r,t){let a=e.data||{},o=((a.raw||{}).analysisResult||{}).riskAnalysis||{},h=o.overallRisk??a.score??a.riskScore??e.score;return {packageName:s,platform:r,name:a.name||a.metadata?.title,version:a.version,status:this.mapStatusFromScore(e.status,h,a.riskLevel),score:h,dependencies:a.dependencies,threats:this.mapThreats(a,o),scanUrl:t.replace(/#.*$/,""),timestamp:new Date(e.timestamp||Date.now()),error:e.error,raw:e}}mapThreats(e,s){let r=s.risks;if(Array.isArray(r)&&r.length>0)return r.map(t=>({type:this.extractThreatType(t.description),severity:this.mapPointsToSeverity(t.points),description:t.description}));if(Array.isArray(e.threats)&&e.threats.length>0)return e.threats.map(t=>({type:typeof t=="string"?t:t.type||t.description,severity:t.severity||"medium",description:t.description}));if(Array.isArray(e.vulnerabilities)&&e.vulnerabilities.length>0)return e.vulnerabilities.flatMap(t=>Array.isArray(t.vulnerabilities)?t.vulnerabilities.map(a=>({type:`${t.component}@${t.version}`,severity:a.severity||"medium",description:a.identifiers?.summary||a.description||`Vulnerability in ${t.component}`})):[{type:t.type||t.component||"vulnerability",severity:t.severity||"medium",description:t.description||t.identifiers?.summary}])}extractThreatType(e){return e?e.includes("vulnerabilities")?"dependency-vulnerability":e.includes("obfuscated")?"obfuscation":e.includes("install count")?"low-adoption":e.includes("Workspace Trust")?"workspace-trust":e.includes("codebase")?"attack-surface":e.includes("Publisher")?"publisher-risk":e.includes("permission")?"permission-risk":"risk-indicator":"unknown"}mapPointsToSeverity(e){return e>=20?"critical":e>=15?"high":e>=10?"medium":"low"}mapStatusFromScore(e,s,r){if(e==="error")return "error";let t=(r||"").toLowerCase();return t==="critical"?"critical":t==="high"?"high":t==="medium"?"medium":t==="low"||t==="safe"?"low":typeof s=="number"?s>=90?"critical":s>=60?"high":s>=40?"medium":"low":"unknown"}async close(){this.browser&&(await this.browser.close(),this.browser=null);}async[Symbol.asyncDispose](){await this.close();}};});p();async function G(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanUrl(n)}finally{await r.close();}}async function L(n,e="chrome",s){let{BrowserTotalScanner:r}=await Promise.resolve().then(()=>(p(),m)),t=new r(s);try{return await t.scanExtension(n,e)}finally{await t.close();}}async function M(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanVSCodeExtension(n)}finally{await r.close();}}async function j(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanJetBrainsPlugin(n)}finally{await r.close();}}async function I(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanNpmPackage(n)}finally{await r.close();}}async function W(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanPyPIPackage(n)}finally{await r.close();}}async function V(n,e){let{BrowserTotalScanner:s}=await Promise.resolve().then(()=>(p(),m)),r=new s(e);try{return await r.scanWordPressPlugin(n)}finally{await r.close();}}export{g as BrowserTotalScanner,L as scanExtension,j as scanJetBrainsPlugin,I as scanNpmPackage,W as scanPyPIPackage,G as scanUrl,M as scanVSCodeExtension,V as scanWordPressPlugin};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browsertotal/scanner",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Scan URLs and extensions using BrowserTotal.com",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",