Repository navigation
feat(vscode): select org explicitly - #83
Open
John-David Dalton (jdalton) wants to merge 1 commit into
Open
John-David Dalton (jdalton) wants to merge 1 commit into
John-David Dalton (jdalton) wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Org selection unused by package fetches
- Package fetches now send the organization resolved by getAuthenticatedContext to the org-scoped purl endpoint when socket-security.orgSlug selects an accessible org.
Or push these changes by commenting:
@cursor push 260a33cf1b
Preview (260a33cf1b)
diff --git a/src/api.mts b/src/api.mts
--- a/src/api.mts
+++ b/src/api.mts
@@ -23,6 +23,7 @@
}
export type PackageRequestOptions = {
+ orgSlug?: string | undefined
signal?: AbortSignal | undefined
timeout?: number | undefined
}
@@ -236,17 +237,25 @@
options?: PackageRequestOptions | undefined,
): AsyncGenerator {
const sdk = createSocketSdk(apiKey, options)
+ const { orgSlug } = {
+ __proto__: null,
+ ...options,
+ } as PackageRequestOptions
const batchSize = 1024
+ const query = { alerts: true, compact: false }
for (let index = 0, { length } = purls; index < length; index += batchSize) {
const components = purls
.slice(index, index + batchSize)
.map(purl => ({ __proto__: null, purl }))
- const result = await sdk.batchPackageFetch(
- { components },
- { alerts: true, compact: false },
- )
+ const result = orgSlug
+ ? await sdk.batchOrgPackageFetch(orgSlug, { components }, query)
+ : await sdk.batchPackageFetch({ components }, query)
if (!result.success) {
- throw createPackageRequestError(result)
+ throw createPackageRequestError({
+ error: '',
+ status: result.status,
+ success: false,
+ })
}
yield* result.data
}
diff --git a/src/ui/purl-alerts-and-scores/manager.mts b/src/ui/purl-alerts-and-scores/manager.mts
--- a/src/ui/purl-alerts-and-scores/manager.mts
+++ b/src/ui/purl-alerts-and-scores/manager.mts
@@ -3,12 +3,15 @@
import { logger } from '../../infra/log.mts'
import path from 'node:path'
import { mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
-import { getAPIKey } from '../../auth.mts'
+import { getAPIKey, getAuthenticatedContext } from '../../auth.mts'
import {
isPackageDisplayDataRenderable,
streamPackageScores,
} from '../../api.mts'
-import type { PackageScoreAndAlerts } from '../../api.mts'
+import type {
+ PackageRequestOptions,
+ PackageScoreAndAlerts,
+} from '../../api.mts'
import { safeDeleteSync } from '@socketsecurity/lib/fs/safe'
import { isObject } from '@socketsecurity/lib/objects/predicates'
import { worstArtifactsByPurl } from './select-artifacts.mts'
@@ -168,6 +171,33 @@
#generation = 0
#contextReady = true
private constructor() {}
+ async #resolvePackageFetchRequest(
+ signal: AbortSignal,
+ timeout: number,
+ ): Promise<{
+ apiKey: string | undefined
+ options: PackageRequestOptions
+ }> {
+ const transport = { signal, timeout }
+ const configuredSlug = vscode.workspace
+ .getConfiguration()
+ .get('socket-security.orgSlug')
+ if (typeof configuredSlug !== 'string' || configuredSlug.length === 0) {
+ return { apiKey: await getAPIKey(), options: transport }
+ }
+ const authenticated = await getAuthenticatedContext()
+ if (!authenticated) {
+ return { apiKey: await getAPIKey(), options: transport }
+ }
+ return {
+ apiKey: authenticated.accessToken,
+ options: {
+ orgSlug: authenticated.organization.slug,
+ signal,
+ timeout,
+ },
+ }
+ }
clear(requeueActive = true) {
this.#generation += 1
if (!requeueActive) {
@@ -280,17 +310,21 @@
)
})
try {
- const apiKey = await getAPIKey()
+ const request = await this.#resolvePackageFetchRequest(
+ controller.signal,
+ this.timeout,
+ )
if (controller.signal.aborted || generation !== this.#generation) {
return
}
// logger.info(`Requesting Socket API for PURLs: ${[...thesePendingUpdates].join(', ')}`)
// Bound the SDK request with the same ceiling the AbortController timer
// uses so a hung connection can't leave entries pending forever.
- const scores = streamPackageScores(apiKey, [...thesePendingUpdates], {
- signal: controller.signal,
- timeout: this.timeout,
- })
+ const scores = streamPackageScores(
+ request.apiKey,
+ [...thesePendingUpdates],
+ request.options,
+ )
// The /v0/purl endpoint can stream MULTIPLE artifacts for the same
// input PURL (e.g. a PyPI sdist and wheel of one version) with
// different scores and alerts. Buffer them all, then collapse to the
diff --git a/test/repo/unit/api.test.mts b/test/repo/unit/api.test.mts
--- a/test/repo/unit/api.test.mts
+++ b/test/repo/unit/api.test.mts
@@ -404,6 +404,22 @@
expect(seen.map(s => s.score.overall)).toEqual([42, 99])
})
+ test('requests the organization purl endpoint for a selected org', async () => {
+ const purl = 'pkg:npm/example-org-policy@1.0.0' as SimPURL
+ const scope = nock(API_ORIGIN)
+ .post('/v0/orgs/example-org/purl', { components: [{ purl }] })
+ .query({ alerts: 'true', compact: 'false' })
+ .reply(200, `${artifactLine(purl, 0.5)}\n`)
+
+ const seen = await collectPackageScores(
+ streamPackageScores(TOKEN, [purl], { orgSlug: 'example-org' }),
+ )
+
+ expect(seen).toHaveLength(1)
+ expect(seen[0]?.score.overall).toBe(0.5)
+ expect(scope.isDone()).toBe(true)
+ })
+
test('requests /v0/purl with the pending purls in the body', async () => {
const purl = 'pkg:npm/express@4.0.0' as SimPURL
let sentBody: unknown
diff --git a/test/repo/unit/ui/purl-alerts-and-scores/manager-cache-requests.test.mts b/test/repo/unit/ui/purl-alerts-and-scores/manager-cache-requests.test.mts
--- a/test/repo/unit/ui/purl-alerts-and-scores/manager-cache-requests.test.mts
+++ b/test/repo/unit/ui/purl-alerts-and-scores/manager-cache-requests.test.mts
@@ -4,11 +4,15 @@
import type { logger as realLogger } from '../../../../../src/infra/log.mts'
import type { SimPURL } from '../../../../../src/ui/externals/parse-externals.mts'
import { PURLDataCache } from '../../../../../src/ui/purl-alerts-and-scores/manager.mts'
+import { setStubWorkspaceState } from '../../../../stubs/vscode.mts'
-const { getAPIKey, streamPackageScores } = vi.hoisted(() => ({
- getAPIKey: vi.fn(),
- streamPackageScores: vi.fn(),
-}))
+const { getAPIKey, getAuthenticatedContext, streamPackageScores } = vi.hoisted(
+ () => ({
+ getAPIKey: vi.fn(),
+ getAuthenticatedContext: vi.fn(),
+ streamPackageScores: vi.fn(),
+ }),
+)
vi.mock(import('node:fs'), async importOriginal => ({
...(await importOriginal()),
@@ -20,7 +24,10 @@
writeFileSync: vi.fn(),
}))
vi.mock(import('../../../../../src/api.mts'), () => ({ streamPackageScores }))
-vi.mock(import('../../../../../src/auth.mts'), () => ({ getAPIKey }))
+vi.mock(import('../../../../../src/auth.mts'), () => ({
+ getAPIKey,
+ getAuthenticatedContext,
+}))
vi.mock(import('../../../../../src/infra/log.mts'), () => ({
logger: {
debug: vi.fn(),
@@ -60,6 +67,8 @@
vi.useFakeTimers()
vi.setSystemTime(new Date('2026-01-01T00:00:00Z'))
getAPIKey.mockReset().mockResolvedValue('test-placeholder-token')
+ getAuthenticatedContext.mockReset().mockResolvedValue(undefined)
+ setStubWorkspaceState({})
streamPackageScores.mockReset().mockImplementation(async function* (
...request: [string, SimPURL[]]
) {
@@ -141,6 +150,37 @@
expect(entry.pkgData).toEqual(packageScore(purl))
})
+ test('sends the selected organization slug with the package request', async () => {
+ setStubWorkspaceState({
+ configuration: { 'socket-security.orgSlug': 'example-org' },
+ })
+ getAuthenticatedContext.mockResolvedValue({
+ accessToken: 'test-placeholder-token',
+ organization: {
+ id: 'example-org',
+ image: undefined,
+ name: 'Example Organization',
+ plan: 'team',
+ slug: 'example-org',
+ },
+ })
+ const purl = 'pkg:npm/example-org-policy@1.0.0'
+ const entry = cache.watch(purl)
+ await flushRequests()
+
+ expect(getAPIKey).not.toHaveBeenCalled()
+ expect(streamPackageScores).toHaveBeenCalledWith(
+ 'test-placeholder-token',
+ [purl],
+ {
+ orgSlug: 'example-org',
+ signal: expect.any(AbortSignal),
+ timeout: cache.timeout,
+ },
+ )
+ expect(entry.pkgData).toEqual(packageScore(purl))
+ })
+
test('uses anonymous lookup without a token and clears the timer', async () => {
getAPIKey.mockResolvedValueOnce(undefined)
const purl = 'pkg:npm/example-no-token@1.0.0'You can send follow-ups to the cloud agent here.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 1555d38. Configure here.
John-David Dalton (jdalton)
force-pushed
the
feat/package-intelligence-vscode-org-selection-delivery
branch
2 times, most recently
from
October 10, 2026 17:48
9c17495 to
f91670d
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
John-David Dalton (jdalton)
force-pushed
the
feat/package-intelligence-vscode-org-selection-delivery
branch
from
October 10, 2026 17:57
f91670d to
98f85dc
Compare
Allow users to choose an accessible organization for package intelligence while keeping existing token-only sessions available. Assisted-by: Codex:GPT-6
John-David Dalton (jdalton)
force-pushed
the
feat/package-intelligence-vscode-org-selection-delivery
branch
from
October 10, 2026 18:02
98f85dc to
ac26ac7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


LLM Description written by Codex: GPT-6
Prepares the VS Code extension for organization-scoped package intelligence: users can select an accessible organization, and credential changes clear stale results. Existing token-only sessions keep working.
This prerequisite does not switch package requests to the new API or apply organization policy to the current package view. Package requests still use the existing API. A separate integration will use the selected organization after the new API is deployed and SDK support is released.
The extension exposes
socket-security.orgSlug, validates the selected slug against accessible organizations, and invalidates stale authentication and manager results when credentials or organization context changes.Pending local gate fixes: the signed commit in this PR's remote head does not yet contain the following corrections. They are committed locally as
a9c3ca85662487ca9aea924f03013df5abc3dfdcbut could not be pushed because mandatory pre-push validation rejected the unrelated base workflow lint issue described below:test-extension,fuzz, andinstall-claude-pluginsscripts.Validation and coverage
Remote head
1555d380413a535fe9022222872d54bf0472bd90had 62 focused tests pass across 5 files, source type check pass, andgit diff --checkpass. Its remote CI setup failed on all operating systems because the hydratedclaude-md-rule-add-guardimporter lacked@socketsecurity/lib-stable@catalog:in the lockfile. The local pending lockfile correction passes frozen install with pinned pnpm 12.10.1.The local pending imports pass script
--describeand--helpchecks, repo source type check, andgit diff --check. Fullpnpm run check:typeson the original remote head was blocked by missing generated ignoredscripts/fleet/process/run-main.mts.The push hook also reports an unrelated lint failure:
.github/workflows/publish-npm.yml: run name must use the validated publish task name contract. Fix the canonical label and sync its copies.This issue is in the base and remains unresolved.pnpm testcould not run because another shared heavy job occupied the host. No real VS Code extension-host test ran; SecretStorage behavior was not verified in the host.