Skip to content
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 changes: 3 additions & 0 deletions app/composables/npm/useAlgoliaSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ interface AlgoliaHit {
deprecated: boolean | string
isDeprecated: boolean
license: string | null
isSecurityHeld: boolean
}

const ATTRIBUTES_TO_RETRIEVE = [
Expand All @@ -67,6 +68,7 @@ const ATTRIBUTES_TO_RETRIEVE = [
'deprecated',
'isDeprecated',
'license',
'isSecurityHeld',
]

const EXISTENCE_CHECK_ATTRS = ['name']
Expand All @@ -90,6 +92,7 @@ function hitToSearchResult(hit: AlgoliaHit): NpmSearchResult {
email: owner.email,
}))
: [],
isSecurityHeld: hit.isSecurityHeld,
},
searchScore: 0,
downloads: {
Expand Down
5 changes: 4 additions & 1 deletion app/pages/search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,17 @@ const { settings } = useSettings()

/**
* Reorder results to put exact package name match at the top,
* and optionally filter out platform-specific packages.
* and optionally filter out platform-specific packages or security holding packages.
*/
const visibleResults = computed(() => {
const raw = rawVisibleResults.value
if (!raw) return raw

let objects = raw.objects

// Filter out "Security holding package" packages taken down by npm registry
objects = objects.filter(r => !r.package.isSecurityHeld)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 Wouldn't it be preferable to implement this filtering in the useAlgoliaSearch or useGlobalSearch composable? Otherwise we'd still be showing security holding packages in other places we use search today (compare page typeahead) and in the future.


// Filter out platform-specific packages if setting is enabled
if (settings.value.hidePlatformPackages) {
objects = objects.filter(r => !isPlatformSpecificPackage(r.package.name))
Expand Down
2 changes: 2 additions & 0 deletions shared/types/npm-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ export interface NpmSearchPackage {
publisher?: NpmSearchPublisher
maintainers?: NpmPerson[]
license?: string
/** Algolia-only: package is an npm-owned security-holder takedown */
isSecurityHeld?: boolean
}

/**
Expand Down
69 changes: 69 additions & 0 deletions test/fixtures/algolia/search/security-holder.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
[
{
"name": "vuln-npm",
"downloadsLast30Days": 0,
"downloadsRatio": 0,
"popular": false,
"version": "0.0.1-security",
"description": "security holding package",
"repository": {
"type": "git",
"url": "npm/security-holder",
"project": "security-holder",
"user": "npm",
"host": "github.com",
"path": "",
"branch": "master"
},
"deprecated": false,
"isDeprecated": false,
"isSecurityHeld": true,
"homepage": null,
"license": null,
"keywords": [],
"modified": 1692592458394,
"owners": [
{
"email": "npm@npmjs.com",
"name": "npm",
"avatar": "https://gravatar.com/avatar/46d8d00e190be647053f7d97fd0478e4",
"link": "https://www.npmjs.com/~npm"
}
],
"objectID": "vuln-npm"
},
{
"name": "npmx-connector",
"downloadsLast30Days": 1047,
"downloadsRatio": 0,
"popular": false,
"version": "0.9.0",
"description": "Local connector for npmx.dev - enables authenticated npm operations from the web UI",
"repository": {
"type": "git",
"url": "https://github.com/npmx-dev/npmx.dev",
"project": "npmx.dev",
"user": "npmx-dev",
"host": "github.com",
"path": "",
"head": "1f574e52f494032683c1aec7f64f6de72d4413a0",
"branch": "1f574e52f494032683c1aec7f64f6de72d4413a0"
},
"deprecated": false,
"isDeprecated": false,
"isSecurityHeld": false,
"homepage": "https://npmx.dev",
"license": "MIT",
"keywords": [],
"modified": 1776194979856,
"owners": [
{
"name": "danielroe",
"email": "daniel@roe.dev",
"avatar": "https://gravatar.com/avatar/f366ee6556b7307a1a2a253c8fb842ca",
"link": "https://www.npmjs.com/~danielroe"
}
],
"objectID": "npmx-connector"
}
]
27 changes: 27 additions & 0 deletions test/nuxt/composables/use-algolia-search.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { describe, expect, it, vi } from 'vitest'
import fixture from '~~/test/fixtures/algolia/search/security-holder.json'

const mockSearch = vi.fn()
vi.mock('algoliasearch/lite', () => ({
liteClient: () => ({ search: mockSearch }),
}))

describe('useAlgoliaSearch', () => {
it('maps isSecurityHeld through to NpmSearchResult.package', async () => {
mockSearch.mockResolvedValue({
results: [{ hits: fixture, nbHits: fixture.length }],
})

const { search } = useAlgoliaSearch()
const { objects } = await search('')

const bad = objects.find(o => o.package.name === 'vuln-npm')
const good = objects.find(o => o.package.name === 'npmx-connector')

expect(bad?.package.isSecurityHeld).toBe(true)
expect(good?.package.isSecurityHeld).toBe(false)

const filtered = objects.filter(o => !o.package.isSecurityHeld).map(o => o.package.name)
expect(filtered).toEqual(['npmx-connector'])
})
})
Loading