Skip to content

Rewrite #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,727 changes: 2,243 additions & 1,484 deletions package-lock.json

Large diffs are not rendered by default.

73 changes: 10 additions & 63 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
},
"icon": "socket-square.png",
"activationEvents": [
"workspaceContains:**/[pP][aA][cC][kK][aA][gG][eE].[jJ][sS][oO][nN]",
"workspaceConatains:**/[pP][aA][cC][kK][aA][gG][eE].[jJ][sS][oO][nN]",
"workspaceContains:**/[sS][oO][cC][kK][eE][tT].[yY][mM][lL]",
"workspaceContains:**/[rR][eE][qQ][uU][iI][rR][eE][mM][eE][nN][tT][sS].[tT][xX][tT]",
"workspaceContains:**/[pP][yY][pP][rR][oO][jJ][eE][cC][tT].[tT][oO][mM][lL]",
Expand All @@ -48,67 +48,19 @@
"main": "./out/main.js",
"homepage": "https://socket.dev",
"contributes": {
"commands": [
{
"command": "socket-security.runReport",
"category": "Socket Security",
"title": "Run Report",
"enablement": "workspaceFolderCount >= 1"
}
],
"configuration": {
"title": "Socket Security",
"properties": {
"socket-security.minIssueLevel": {
"order": 1,
"type": "string",
"default": "low",
"enum": [
"critical",
"high",
"middle",
"low"
],
"description": "Hide all issues that are less important than this level. Note some issues are hidden by default so you may also wish to enable showing all issue types still."
},
"socket-security.errorOverlayThreshold": {
"order": 2,
"type": "number",
"default": 50,
"minimum": 0,
"maximum": 100,
"description": "Show error overlay for any import of a package with a summary score less than this value.",
"examples": [
50
]
},
"socket-security.warnOverlayThreshold": {
"order": 3,
"type": "number",
"default": 80,
"minimum": 0,
"maximum": 100,
"description": "Show overlay for any import of a package with a summary score less than this value.",
"examples": [
80
]
},
"socket-security.reportsEnabled": {
"order": 4,
"type": "boolean",
"default": true,
"description": "Create reports from package manifest files (package.json / package-lock.json) that require sending data remotely. Disabling this will disable all issues but keep scores listed."
},
"socket-security.pythonInterpreter": {
"order": 5,
"order": 1,
"type": "string",
"description": "Path to a Python interpreter to use for Socket dependency analysis.",
"examples": [
"/usr/bin/python"
]
},
"socket-security.goExecutable": {
"order": 6,
"order": 2,
"type": "string",
"description": "Path to a Go executable to use for Socket dependency analysis.",
"examples": [
Expand All @@ -124,7 +76,7 @@
"publisher": "SocketSecurity",
"scripts": {
"vscode:prepublish": "npm run esbuild -- --minify",
"esbuild-base": "esbuild --bundle --external:vscode --loader:.wasm=binary --loader:.go=file --outdir=out/ --platform=node --sourcemap",
"esbuild-base": "esbuild --bundle --external:vscode --loader:.wasm=binary --loader:.go=file --loader:.py=text --outdir=out/ --platform=node --sourcemap",
"esbuild": "npm run esbuild-base -- --format=cjs main=src/extension.ts",
"test-compile": "tsc -p ./",
"lint": "eslint \"src/**/*.ts\"",
Expand All @@ -133,23 +85,18 @@
},
"dependencies": {
"@babel/parser": "^7.20.7",
"@babel/traverse": "^7.20.7",
"@babel/types": "^7.20.7",
"@socketsecurity/config": "^2.0.0",
"@socketsecurity/registry": "^1.0.66",
"@vscode/vsce": "^2.20.1",
"acorn-walk": "^8.2.0",
"antlr4": "^4.13.0",
"ast-types": "^0.14.2",
"form-data-encoder": "^3.0.0",
"formdata-node": "^5.0.1",
"@vscode/python-extension": "^1.0.5",
"@vscode/vsce": "^3.6.0",
"ini": "^3.0.1",
"json-to-ast": "^2.1.0",
"micromatch": "^4.0.8",
"octokit": "^3.1.2",
"safe-stable-stringify": "^2.4.1",
"semver": "^7.5.2",
"yaml": "^2.2.2"
"octokit": "^3.1.2"
},
"devDependencies": {
"@types/babel__traverse": "^7.20.7",
"@types/ini": "^1.3.31",
"@types/json-to-ast": "^2.1.2",
"@types/micromatch": "^4.0.2",
Expand Down
25 changes: 25 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/////////
// DESIGN NOTES
/////////
//
// We pass apiKeys rather than shared state to avoid certain races, so if a
// workflow starts with 1 API key it is inconvenient to grab an implicitly
// new api key in the middle of the workflow
//
function toAuthHeader(apiKey: string) {
return `Basic ${Buffer.from(`${apiKey}:`).toString('base64url')}`
}
export async function getQuota(apiKey: string) {
const res = await fetch('https://api.socket.dev/v0/settings', {
method: 'POST',
headers: {
Authorization: toAuthHeader(apiKey),
'Content-Type': 'application/json'
}
})
if (res.ok) {
return res.json()
} else {
throw new Error(await res.text())
}
}
183 changes: 183 additions & 0 deletions src/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import vscode from 'vscode'
import os from 'os'
import path from 'path'
import { DIAGNOSTIC_SOURCE_STR, EXTENSION_PREFIX } from './util'
import { getQuota } from './api'

export async function activate(context: vscode.ExtensionContext, disposables?: Array<vscode.Disposable>) {
//#region file path/watching
// responsible for watching files to know when to sync from disk
let dataHome = process.platform === 'win32'
? process.env['LOCALAPPDATA']
: process.env['XDG_DATA_HOME']

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']
))
}

let defaultSettingsPath = path.join(dataHome, 'socket', 'settings')
let settingsPath = vscode.workspace.getConfiguration(EXTENSION_PREFIX)
.get('settingsFile', defaultSettingsPath)
//#endregion
//#region session sync
// responsible for keeping disk an mem in sync
const PUBLIC_TOKEN = 'sktsec_t_--RAN5U4ivauy4w37-6aoKyYPDt5ZbaT5JBVMqiwKo_api'
let liveSessions: Map<vscode.AuthenticationSession['accessToken'], vscode.AuthenticationSession> = new Map()
const emitter = new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>()
async function syncLiveSessionsFromDisk() {
const settings_on_disk = JSON.parse(Buffer.from(
new TextDecoder().decode(await vscode.workspace.fs.readFile(vscode.Uri.file(settingsPath))),
'base64'
).toString('utf8'))
const {
apiKey
} = settings_on_disk
const sessionOnDisk: typeof liveSessions = new Map<vscode.AuthenticationSession['accessToken'], vscode.AuthenticationSession>()
if (apiKey) {
sessionOnDisk.set(
apiKey,
{
accessToken: apiKey,
id: apiKey,
account: {
id: apiKey,
label: `API Key for ${DIAGNOSTIC_SOURCE_STR}`
},
scopes: [],
}
)
}
let added: Array<vscode.AuthenticationSession> = []
let changed: Array<vscode.AuthenticationSession> = []
let removed: Array<vscode.AuthenticationSession> = []
for (const diskSession of sessionOnDisk.values()) {
// already have this access token in mem session
if (liveSessions.has(diskSession.accessToken)) {
const liveSession = liveSessions.get(diskSession.accessToken)
liveSessions.delete(diskSession.accessToken)
// mem has same as what is on disk
if (JSON.stringify(liveSession) !== JSON.stringify(diskSession)) {
continue
}
changed.push(diskSession)
} else {
added.push(diskSession)
}
}
for (const liveSessionWithoutDiskSession of liveSessions.values()) {
removed.push(liveSessionWithoutDiskSession)
}
liveSessions = sessionOnDisk
if (added.length + changed.length + removed.length > 0) {
emitter.fire({
added,
changed,
removed
})
}
}
async function syncLiveSessionsToDisk() {
const contents = Buffer.from(
JSON.stringify(
Array.from(liveSessions.values(), s => ({
apiKey: s.accessToken
})),
null,
2
)
).toString('base64')
return vscode.workspace.fs.writeFile(vscode.Uri.file(settingsPath), new TextEncoder().encode(contents))
}
await syncLiveSessionsFromDisk()
//#endregion
//#region service glue
const service = vscode.authentication.registerAuthenticationProvider(`${EXTENSION_PREFIX}`, `${DIAGNOSTIC_SOURCE_STR}`, {
onDidChangeSessions(fn) {
return emitter.event(fn)
},
async getSessions(scopes: readonly string[] | undefined, options: vscode.AuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession[]> {
return Array.from(liveSessions.values())
},
async createSession(scopes: readonly string[], options: vscode.AuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession> {
const realLogin = `Log in to ${DIAGNOSTIC_SOURCE_STR}`
const publicLogin = `Use public token for ${DIAGNOSTIC_SOURCE_STR}`
const res = await vscode.window.showQuickPick([
realLogin,
publicLogin,
])
if (!res) {
throw new Error(`Cancelled creation of session for ${DIAGNOSTIC_SOURCE_STR}`)
}
let apiKey: string
if (res === publicLogin) {
apiKey = ''
} else {
let keyInfo: string
let maybeApiKey = await vscode.window.showInputBox({
title: 'Socket Security API Token',
placeHolder: 'Leave this blank to use public demo token',
prompt: 'Enter your API token from https://socket.dev/',
async validateInput (value) {
if (!value) return
keyInfo = (await getQuota(value))!
if (!keyInfo) return 'Unable to validate API key'
}
})
// cancelled
if (maybeApiKey === undefined) {
throw new Error(`Cancelled creation of session for ${DIAGNOSTIC_SOURCE_STR}`)
}
apiKey = maybeApiKey
}
if (apiKey === '') {
apiKey = PUBLIC_TOKEN
}
const session = {
accessToken: apiKey,
id: apiKey,
account: {
id: apiKey,
label: `API Key for ${DIAGNOSTIC_SOURCE_STR}`
},
scopes: [],
}
let oldSessions = Array.from(liveSessions.values())
liveSessions = new Map([
[apiKey, session]
])
emitter.fire({
added: [session],
changed: [],
removed: oldSessions
})
await syncLiveSessionsToDisk()
return session
},
async removeSession(sessionId: string): Promise<void> {
const session = liveSessions.get(sessionId)
if (session) {
emitter.fire({
added: [],
changed: [],
removed: [session]
})
await syncLiveSessionsToDisk()
}
}
})
context.subscriptions.push(service)
vscode.commands.registerCommand(`${EXTENSION_PREFIX}.login`, () => {
vscode.authentication.getSession(`${EXTENSION_PREFIX}`, [], {
createIfNone: true,
})
})
//#endregion
return {

}
}
2 changes: 1 addition & 1 deletion src/data/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export async function sniffForGithubOrgOrUser(workspaceRootURI: vscode.Uri): Pro
} catch (e) {}
}

export function installGithubApp(uri: vscode.Uri) {
export function installGithubApp(uri: vscode.Uri) {return
vscode.authentication.getSession('github', [
'read:user',
'read:org'
Expand Down
46 changes: 33 additions & 13 deletions src/data/go/executable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,47 @@ export async function initGo(): Promise<vscode.Disposable> {

const warned = new Set<string>();

export async function getGoExecutable(fileName?: string): Promise<string | void> {
export async function getGoExecutable(fileName?: string): Promise<{execPath: string} | void> {
// no executable in virtual workspace
if (vscode.workspace.workspaceFolders?.every(f => f.uri.scheme !== 'file')) return
let execPath: string = 'go';
let usingSystemPath = true
const workspaceConfig = vscode.workspace.getConfiguration(EXTENSION_PREFIX);
const pathOverride = workspaceConfig.get<string>('goExecutable');
if (pathOverride) {
return Promise.resolve(vscode.workspace.fs.stat(vscode.Uri.file(pathOverride))).then(
st => {
if (st.type & vscode.FileType.File) return pathOverride;
try {
const st = await vscode.workspace.fs.stat(vscode.Uri.file(pathOverride))
if (st.type & vscode.FileType.File) {
usingSystemPath = false;
execPath = pathOverride;
} else {
throw new Error('not a file')
}
).catch(err => {
vscode.window.showErrorMessage(`Failed to find Go binary at '${pathOverride}'. Please update ${EXTENSION_PREFIX}.goExecutable.`)
})
} catch {
}
if (usingSystemPath) {
vscode.window.showErrorMessage(`Failed to find Go binary at '${pathOverride}'. Please update ${EXTENSION_PREFIX}.pythonInterpreter.`);
}
}
if (usingSystemPath) {
const ext = await getGoExtension();
const cmd = await ext?.settings.getExecutionCommand(
'go',
fileName && vscode.Uri.file(fileName)
)
if (cmd) {
usingSystemPath = false;
execPath = cmd.binPath
} else {
// TODO: make this less noisy
// warnToInstallMoreReliableGo(ext);
}
}
const ext = await getGoExtension();
const cmd = await ext?.settings.getExecutionCommand(
'go',
fileName && vscode.Uri.file(fileName)
)
if (cmd) return cmd.binPath
return {execPath}
}

function warnToInstallMoreReliableGo(ext: vscode.Extension<any>) {
const workspaceConfig = vscode.workspace.getConfiguration(EXTENSION_PREFIX);
const workspaceID = vscode.workspace.name ||
vscode.workspace.workspaceFolders?.map(f => f.uri.fsPath).join(',') ||
vscode.window.activeTextEditor?.document.uri.fsPath;
Expand Down
Loading