@socketsecurity/bun-security-scanner
Version:
Bun security scanner for SocketDev
106 lines (82 loc) • 3.18 kB
text/typescript
import Bun from 'bun'
import path from 'node:path'
import os from 'node:os'
import authenticated from './modes/authenticated'
import unauthenticated from './modes/unauthenticated'
let SOCKET_API_KEY = process.env.SOCKET_API_KEY
if (typeof SOCKET_API_KEY !== 'string') {
// get OS app data directory
let dataHome = process.platform === 'win32'
? Bun.env.LOCALAPPDATA
: Bun.env.XDG_DATA_HOME
// fallback
if (!dataHome) {
if (process.platform === 'win32') throw new Error('missing %LOCALAPPDATA%')
const home = os.homedir()
dataHome = path.join(home, ...(process.platform === 'darwin'
? ['Library', 'Application Support']
: ['.local', 'share']
))
}
// append `socket/settings`
const defaultSettingsPath = path.join(dataHome, 'socket', 'settings')
const file = Bun.file(defaultSettingsPath)
// attempt to read token from socket settings
if (await file.exists()) {
const rawContent = await file.text()
// rawContent is base64, must decode
try {
SOCKET_API_KEY = JSON.parse(Buffer.from(rawContent, 'base64').toString().trim()).apiToken
} catch {
throw new Error('error reading Socket settings')
}
}
}
if (!SOCKET_API_KEY) {
console.log(`⚠ Socket Security Scanner free mode. Set SOCKET_API_KEY to use your Socket org settings.`)
}
const scannerImplementation = SOCKET_API_KEY ? authenticated(SOCKET_API_KEY) : unauthenticated()
const purlRegex = /^pkg:npm\/((?:@[^/]+\/)?(?:[^@]+))@(.+)$/
export const scanner: Bun.Security.Scanner = {
version: '1',
async scan({ packages }: { packages: Array<Bun.Security.Package> }) {
const results: Bun.Security.Advisory[] = []
while (packages.length) {
const scanResults = scannerImplementation(packages)
for await (const artifacts of scanResults) {
for (const artifact of artifacts) {
if (artifact.alerts && artifact.alerts.length > 0) {
for (const alert of artifact.alerts) {
const description = ['']
if (alert.type === 'didYouMean') {
description.push(`This package could be a typo-squatting attempt of another package (${alert.props.alternatePackage}).`)
}
if (alert.props.description) {
description.push(alert.props.description)
}
if (alert.props.note) {
description.push(alert.props.note)
}
const fix = alert.fix?.description
if (fix) {
description.push(`Fix: ${fix}`)
}
const match = artifact.inputPurl.match(purlRegex);
if (!match) continue;
const name = match[1];
const version = match[2];
const url = `https://socket.dev/npm/package/${name}/overview/${version}`
results.push({
level: alert.action === 'error' ? 'fatal' : 'warn',
package: artifact.inputPurl,
url,
description: description.join('\n\n') + '\n'
})
}
}
}
}
}
return results
}
}