From bfe1e357d64bbb0b8c8ed7d512b79e562e2e5f7f Mon Sep 17 00:00:00 2001 From: Qiuner Date: Tue, 18 Aug 2026 23:49:44 +0800 Subject: [PATCH] test: add self-verifying coverage for boundaries --- .../tests/install-target-adapter.spec.ts | 52 +++++++++++- .../tests/media-service.spec.ts | 43 ++++++++++ .../tests/module-resolution.spec.ts | 84 +++++++++++++++++++ 3 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 dsh-plugin-desktop/tests/module-resolution.spec.ts diff --git a/dsh-community-market/tests/install-target-adapter.spec.ts b/dsh-community-market/tests/install-target-adapter.spec.ts index 01c971e3fa..e81ea994fe 100644 --- a/dsh-community-market/tests/install-target-adapter.spec.ts +++ b/dsh-community-market/tests/install-target-adapter.spec.ts @@ -26,12 +26,12 @@ const baseItem = { description: { en: 'A better sidebar.' }, } -async function adapt(installMethods: readonly unknown[]) { +async function adapt(installMethods: readonly unknown[], itemOverrides: Record = {}) { const http: CatalogHttpClient = { getJson: vi.fn(async () => ({ value: { meta: { revision: 'sha256:fixture' }, - packages: [{ ...baseItem, installMethods }], + packages: [{ ...baseItem, ...itemOverrides, installMethods }], }, finalUrl: 'https://deepseek1024.com/api/v1/plugins', })), @@ -109,6 +109,54 @@ describe('1024Store install target normalization', () => { expect(snapshot.items[0]).not.toHaveProperty('latestVersion') }) + it('accepts duplicate evidence for the same reviewed npm target without treating it as ambiguity', async () => { + const snapshot = await adapt([ + { + kind: 'npm', + spec: 'dsh-better-sidebar', + verification: 'verified', + code: 'repository_backlink', + requiresBuildAllowance: false, + revision: '0.12.3', + }, + { + kind: 'npm', + spec: 'dsh-better-sidebar', + command: 'still ignored', + verification: 'verified', + code: 'repository_backlink', + requiresBuildAllowance: false, + revision: '0.12.3', + }, + ]) + + expect(snapshot.items[0]).toMatchObject({ + latestVersion: '0.12.3', + package: { registry: 'npm', name: 'dsh-better-sidebar' }, + }) + expect(JSON.stringify(snapshot)).not.toContain('still ignored') + }) + + it.each([ + ['non-GitHub repository', { url: 'https://gitlab.example/omdsh-dev/DSH-better-sidebar' }], + ['repository with extra path segments', { url: 'https://github.com/omdsh-dev/DSH-better-sidebar/releases' }], + ['repository with credentials', { url: 'https://user@github.com/omdsh-dev/DSH-better-sidebar' }], + ['repository with query text', { url: 'https://github.com/omdsh-dev/DSH-better-sidebar?tab=readme' }], + ['control character in item id', { id: 'omdsh-dev/DSH-better-sidebar\u0000hidden' }], + ] as const)('drops a catalog item with %s', async (_label, itemOverrides) => { + const snapshot = await adapt([{ + kind: 'npm', + spec: 'dsh-better-sidebar', + verification: 'verified', + code: 'repository_backlink', + requiresBuildAllowance: false, + revision: '0.12.3', + }], itemOverrides) + + expect(snapshot.items).toEqual([]) + expect(snapshot.page).toEqual({ total: 0 }) + }) + it('scans the full normalized catalog beyond discovery page one with one registry read', async () => { const packages = Array.from({ length: 205 }, (_, index) => { const suffix = String(index).padStart(3, '0') diff --git a/dsh-community-market/tests/media-service.spec.ts b/dsh-community-market/tests/media-service.spec.ts index 2fa51a57bb..f39f69a4dd 100644 --- a/dsh-community-market/tests/media-service.spec.ts +++ b/dsh-community-market/tests/media-service.spec.ts @@ -6,6 +6,8 @@ import { createRestrictedImageFetcher, marketMediaAssetUrl, normalizeMarketImage, + normalizeAllowedHostnames, + validateRemoteImageUrl, type MarketMediaCandidate, } from '../src/media/index.js' @@ -156,6 +158,47 @@ describe('market media service', () => { }) describe('restricted market image fetcher', () => { + it('normalizes reviewed hostnames without accepting wildcards, ports, paths, or IP literals', () => { + expect(normalizeAllowedHostnames([ + 'avatars.githubusercontent.com', + 'GitHub.com', + 'github.com', + ])).toEqual(['avatars.githubusercontent.com', 'github.com']) + + for (const value of [ + '', + '*.github.com', + 'github.com/avatar.png', + 'github.com:443', + '127.0.0.1', + 'github.com@attacker.example', + ]) { + expect(() => normalizeAllowedHostnames([value]), value) + .toThrow(MarketMediaError) + } + }) + + it('validates remote image URLs against the exact reviewed host set', () => { + const allowed = new Set(['github.com', 'avatars.githubusercontent.com']) + + expect(validateRemoteImageUrl('https://github.com/anywhere-labs.png?size=128', allowed).href) + .toBe('https://github.com/anywhere-labs.png?size=128') + expect(validateRemoteImageUrl('https://github.com:443/anywhere-labs.png', allowed).href) + .toBe('https://github.com/anywhere-labs.png') + + for (const value of [ + 'http://github.com/anywhere-labs.png', + 'https://github.com:444/anywhere-labs.png', + 'https://user:pass@github.com/anywhere-labs.png', + 'https://github.com/anywhere-labs.png#fragment', + 'https://github.com.attacker.example/anywhere-labs.png', + 'not a url', + ]) { + expect(() => validateRemoteImageUrl(value, allowed), value) + .toThrow(MarketMediaError) + } + }) + it('pins every redirect DNS lookup and permits only adapter-reviewed hosts', async () => { const lookupAddresses = vi.fn(async (_hostname: string) => [{ address: '93.184.216.34', family: 4 as const }]) const request = vi.fn() diff --git a/dsh-plugin-desktop/tests/module-resolution.spec.ts b/dsh-plugin-desktop/tests/module-resolution.spec.ts new file mode 100644 index 0000000000..18831038ff --- /dev/null +++ b/dsh-plugin-desktop/tests/module-resolution.spec.ts @@ -0,0 +1,84 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +const hooks = vi.hoisted(() => ({ + resolve: undefined as undefined | (( + specifier: string, + context: { parentURL?: string }, + nextResolve: (specifier: string, context: { parentURL?: string }) => unknown, + ) => unknown), + deregister: vi.fn(), +})) + +vi.mock('node:module', () => ({ + registerHooks: vi.fn((definition: { resolve: typeof hooks.resolve }) => { + hooks.resolve = definition.resolve + return { deregister: hooks.deregister } + }), +})) + +const { installProfilePackageResolver } = await import('../src/module-resolution.ts') + +describe('installProfilePackageResolver', () => { + beforeEach(() => { + hooks.resolve = undefined + hooks.deregister.mockClear() + }) + + it('routes Loader bare imports through the selected profile and keeps relative imports unchanged', async () => { + const profileBaseUrl = 'file:///C:/Users/test/profile/' + const dispose = installProfilePackageResolver(profileBaseUrl) + const nextResolve = vi.fn(async (specifier: string, context: { parentURL?: string }) => ({ + specifier, + context, + })) + const loaderEntryUrl = import.meta.resolve('@deepseek-ai/cordis-plugin-loader') + + expect(hooks.resolve?.( + 'dsh-plugin-desktop', + { parentURL: loaderEntryUrl }, + nextResolve, + )).toEqual({ + shortCircuit: true, + url: new URL('../lib/index.js', new URL('../src/module-resolution.ts', import.meta.url)).href, + }) + + await expect(hooks.resolve?.( + 'left-pad', + { parentURL: loaderEntryUrl }, + nextResolve, + )).resolves.toEqual({ + specifier: 'left-pad', + context: { parentURL: profileBaseUrl }, + }) + + await expect(hooks.resolve?.( + './relative.js', + { parentURL: loaderEntryUrl }, + nextResolve, + )).resolves.toEqual({ + specifier: './relative.js', + context: { parentURL: loaderEntryUrl }, + }) + + await expect(hooks.resolve?.( + 'left-pad', + { parentURL: 'file:///C:/Users/test/other.js' }, + nextResolve, + )).resolves.toEqual({ + specifier: 'left-pad', + context: { parentURL: 'file:///C:/Users/test/other.js' }, + }) + + dispose() + expect(hooks.deregister).toHaveBeenCalledTimes(1) + }) + + it('deregisters hooks only once even if the disposer is reused', () => { + const dispose = installProfilePackageResolver('file:///C:/Users/test/profile/') + + dispose() + dispose() + + expect(hooks.deregister).toHaveBeenCalledTimes(1) + }) +})