Skip to content
Merged
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
2 changes: 1 addition & 1 deletion packages/cli/src/commands/scan/create-scan-from-github.mts
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ async function testAndDownloadManifestFile({
orgGithub: string
repoSlug: string
supportedFiles:
| SocketSdkSuccessResult<'getReportSupportedFiles'>['data']
| SocketSdkSuccessResult<'getSupportedFiles'>['data']
| undefined
tmpDir: string
}): Promise<CResult<{ isManifest: boolean }>> {
Expand Down
20 changes: 16 additions & 4 deletions packages/cli/src/commands/scan/fetch-supported-scan-file-names.mts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getDefaultOrgSlug } from '../ci/fetch-default-org-slug.mjs'
import { handleApiCall } from '../../utils/socket/api.mjs'
import { setupSdk } from '../../utils/socket/sdk.mjs'

Expand All @@ -7,14 +8,15 @@ import type { Spinner } from '@socketsecurity/lib/spinner'
import type { SocketSdkSuccessResult } from '@socketsecurity/sdk'

export type FetchSupportedScanFileNamesOptions = {
orgSlug?: string | undefined
sdkOpts?: SetupSdkOptions | undefined
spinner?: Spinner | undefined
}

export async function fetchSupportedScanFileNames(
options?: FetchSupportedScanFileNamesOptions | undefined,
): Promise<CResult<SocketSdkSuccessResult<'getReportSupportedFiles'>['data']>> {
const { sdkOpts, spinner } = {
): Promise<CResult<SocketSdkSuccessResult<'getSupportedFiles'>['data']>> {
const { orgSlug, sdkOpts, spinner } = {
__proto__: null,
...options,
} as FetchSupportedScanFileNamesOptions
Expand All @@ -25,8 +27,18 @@ export async function fetchSupportedScanFileNames(
}
const sockSdk = sockSdkCResult.data

return await handleApiCall<'getReportSupportedFiles'>(
sockSdk.getSupportedScanFiles(),
// Use provided orgSlug or discover it.
let resolvedOrgSlug = orgSlug
if (!resolvedOrgSlug) {
const orgSlugCResult = await getDefaultOrgSlug()
if (!orgSlugCResult.ok) {
return orgSlugCResult
}
resolvedOrgSlug = orgSlugCResult.data
}

return await handleApiCall<'getSupportedFiles'>(
sockSdk.getSupportedFiles(resolvedOrgSlug),
{
description: 'supported scan file types',
spinner,
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/scan/handle-create-new-scan.mts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export async function handleCreateNewScan({

const spinner = getDefaultSpinner()

const supportedFilesCResult = await fetchSupportedScanFileNames({ spinner })
const supportedFilesCResult = await fetchSupportedScanFileNames({ orgSlug, spinner })
if (!supportedFilesCResult.ok) {
debug('warn', 'Failed to fetch supported scan file names')
debugDir('inspect', { supportedFilesCResult })
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/src/utils/fs/glob.mts
Original file line number Diff line number Diff line change
Expand Up @@ -159,22 +159,22 @@ function workspacePatternToGlobPattern(workspace: string): string {

export function filterBySupportedScanFiles(
filepaths: string[] | readonly string[],
supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],
supportedFiles: SocketSdkSuccessResult<'getSupportedFiles'>['data'],
): string[] {
const patterns = getSupportedFilePatterns(supportedFiles)
return filepaths.filter(p => micromatch.some(p, patterns, { dot: true }))
}

export function createSupportedFilesFilter(
supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],
supportedFiles: SocketSdkSuccessResult<'getSupportedFiles'>['data'],
): (filepath: string) => boolean {
const patterns = getSupportedFilePatterns(supportedFiles)
return (filepath: string) =>
micromatch.some(filepath, patterns, { dot: true })
}

export function getSupportedFilePatterns(
supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],
supportedFiles: SocketSdkSuccessResult<'getSupportedFiles'>['data'],
): string[] {
const patterns: string[] = []
for (const key of Object.keys(supportedFiles)) {
Expand Down Expand Up @@ -309,7 +309,7 @@ export async function globWorkspace(

export function isReportSupportedFile(
filepath: string,
supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],
supportedFiles: SocketSdkSuccessResult<'getSupportedFiles'>['data'],
) {
const patterns = getSupportedFilePatterns(supportedFiles)
return micromatch.some(filepath, patterns, { dot: true })
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/utils/fs/path-resolve.mts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export type PackageFilesForScanOptions = {

export async function getPackageFilesForScan(
inputPaths: string[],
supportedFiles: SocketSdkSuccessResult<'getReportSupportedFiles'>['data'],
supportedFiles: SocketSdkSuccessResult<'getSupportedFiles'>['data'],
options?: PackageFilesForScanOptions | undefined,
): Promise<string[]> {
const { config: socketConfig, cwd = process.cwd() } = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,8 @@
/**
* Unit tests for fetchSupportedScanFileNames.
*
* Purpose:
* Tests fetching supported manifest file names for scanning. Validates which files Socket can analyze.
*
* Test Coverage:
* - Successful API operation
* - SDK setup failure handling
* - API call error scenarios
* - Custom SDK options (API tokens, base URLs)
* - Supported file types
* - Ecosystem detection
* - Null prototype usage for security
*
* Testing Approach:
* Uses SDK test helpers to mock Socket API interactions. Validates comprehensive
* error handling and API integration.
*
* Related Files:
* - src/commands/SupportedScanFileNames.mts (implementation)
* Tests fetching supported manifest file names for scanning.
* Validates which files Socket can analyze via the SDK v4 getSupportedFiles API.
*/

import { describe, expect, it, vi } from 'vitest'
Expand Down Expand Up @@ -51,13 +35,13 @@ describe('fetchSupportedScanFileNames', () => {
}

const { mockHandleApi, mockSdk } = await setupSdkMockSuccess(
'getSupportedScanFiles',
'getSupportedFiles',
mockData,
)

const result = await fetchSupportedScanFileNames()
const result = await fetchSupportedScanFileNames({ orgSlug: 'test-org' })

expect(mockSdk.getSupportedScanFiles).toHaveBeenCalledWith()
expect(mockSdk.getSupportedFiles).toHaveBeenCalledWith('test-org')
expect(mockHandleApi).toHaveBeenCalledWith(expect.any(Promise), {
description: 'supported scan file types',
})
Expand All @@ -74,7 +58,7 @@ describe('fetchSupportedScanFileNames', () => {
cause: 'Invalid configuration',
})

const result = await fetchSupportedScanFileNames()
const result = await fetchSupportedScanFileNames({ orgSlug: 'test-org' })

expect(result.ok).toBe(false)
expect(result.message).toBe('Failed to setup SDK')
Expand All @@ -85,9 +69,9 @@ describe('fetchSupportedScanFileNames', () => {
const { fetchSupportedScanFileNames } =
await import('../../../../../src/commands/scan/fetch-supported-scan-file-names.mts')

await setupSdkMockError('getSupportedScanFiles', 'API error', 500)
await setupSdkMockError('getSupportedFiles', 'API error', 500)

const result = await fetchSupportedScanFileNames()
const result = await fetchSupportedScanFileNames({ orgSlug: 'test-org' })

expect(result.ok).toBe(false)
expect(result.code).toBe(500)
Expand All @@ -98,29 +82,31 @@ describe('fetchSupportedScanFileNames', () => {
await import('../../../../../src/commands/scan/fetch-supported-scan-file-names.mts')

const { mockSdk, mockSetupSdk } = await setupSdkMockSuccess(
'getSupportedScanFiles',
'getSupportedFiles',
{},
)

const options = {
await fetchSupportedScanFileNames({
orgSlug: 'my-org',
sdkOpts: {
apiToken: 'custom-token',
baseUrl: 'https://api.example.com',
},
}

await fetchSupportedScanFileNames(options)
})

expect(mockSetupSdk).toHaveBeenCalledWith(options.sdkOpts)
expect(mockSdk.getSupportedScanFiles).toHaveBeenCalledWith()
expect(mockSetupSdk).toHaveBeenCalledWith({
apiToken: 'custom-token',
baseUrl: 'https://api.example.com',
})
expect(mockSdk.getSupportedFiles).toHaveBeenCalledWith('my-org')
})

it('passes custom spinner', async () => {
const { fetchSupportedScanFileNames } =
await import('../../../../../src/commands/scan/fetch-supported-scan-file-names.mts')

const { mockHandleApi } = await setupSdkMockSuccess(
'getSupportedScanFiles',
'getSupportedFiles',
{},
)

Expand All @@ -131,11 +117,7 @@ describe('fetchSupportedScanFileNames', () => {
fail: vi.fn(),
}

const options = {
spinner: mockSpinner,
}

await fetchSupportedScanFileNames(options)
await fetchSupportedScanFileNames({ orgSlug: 'test-org', spinner: mockSpinner })

expect(mockHandleApi).toHaveBeenCalledWith(expect.any(Promise), {
description: 'supported scan file types',
Expand All @@ -147,76 +129,28 @@ describe('fetchSupportedScanFileNames', () => {
const { fetchSupportedScanFileNames } =
await import('../../../../../src/commands/scan/fetch-supported-scan-file-names.mts')

await setupSdkMockSuccess('getSupportedScanFiles', {
await setupSdkMockSuccess('getSupportedFiles', {
supportedFiles: [],
ecosystems: [],
})

const result = await fetchSupportedScanFileNames()
const result = await fetchSupportedScanFileNames({ orgSlug: 'test-org' })

expect(result.ok).toBe(true)
expect(result.data?.supportedFiles).toEqual([])
expect(result.data?.ecosystems).toEqual([])
})

it('handles comprehensive file types', async () => {
const { fetchSupportedScanFileNames } =
await import('../../../../../src/commands/scan/fetch-supported-scan-file-names.mts')

const comprehensiveFiles = [
// JavaScript/Node.js
'package.json',
'package-lock.json',
'yarn.lock',
'pnpm-lock.yaml',
// PHP
'composer.json',
'composer.lock',
// Ruby
'Gemfile',
'Gemfile.lock',
// Python
'requirements.txt',
'Pipfile',
'Pipfile.lock',
'poetry.lock',
'pyproject.toml',
// Go
'go.mod',
'go.sum',
// Java
'pom.xml',
'build.gradle',
// .NET
'packages.config',
'*.csproj',
// Rust
'Cargo.toml',
'Cargo.lock',
]

await setupSdkMockSuccess('getSupportedScanFiles', {
supportedFiles: comprehensiveFiles,
})

const result = await fetchSupportedScanFileNames()

expect(result.ok).toBe(true)
expect(result.data?.supportedFiles).toContain('package.json')
expect(result.data?.supportedFiles).toContain('Cargo.toml')
expect(result.data?.supportedFiles).toContain('pom.xml')
})

it('works without options parameter', async () => {
it('works with orgSlug provided', async () => {
const { fetchSupportedScanFileNames } =
await import('../../../../../src/commands/scan/fetch-supported-scan-file-names.mts')

const { mockHandleApi, mockSetupSdk } = await setupSdkMockSuccess(
'getSupportedScanFiles',
'getSupportedFiles',
{ supportedFiles: ['package.json'] },
)

const result = await fetchSupportedScanFileNames()
const result = await fetchSupportedScanFileNames({ orgSlug: 'test-org' })

expect(mockSetupSdk).toHaveBeenCalledWith(undefined)
expect(mockHandleApi).toHaveBeenCalledWith(expect.any(Promise), {
Expand All @@ -230,12 +164,10 @@ describe('fetchSupportedScanFileNames', () => {
const { fetchSupportedScanFileNames } =
await import('../../../../../src/commands/scan/fetch-supported-scan-file-names.mts')

const { mockSdk } = await setupSdkMockSuccess('getSupportedScanFiles', {})
const { mockSdk } = await setupSdkMockSuccess('getSupportedFiles', {})

// This tests that the function properly uses __proto__: null.
await fetchSupportedScanFileNames()
await fetchSupportedScanFileNames({ orgSlug: 'test-org' })

// The function should work without prototype pollution issues.
expect(mockSdk.getSupportedScanFiles).toHaveBeenCalled()
expect(mockSdk.getSupportedFiles).toHaveBeenCalled()
})
})
Loading