fix: patch Trivy CRITICAL/HIGH findings in container image

- apk upgrade in runtime stage to fix OpenSSL CVEs (CVE-2025-15467 CRITICAL, CVE-2025-69419 HIGH)
- remove npm from runtime image to eliminate npm-bundled CVEs
- update fastify to 5.8.5 and force via pnpm override (CVE-2026-33806 HIGH)
- switch vitest pool to threads to fix flaky audio extractor tests
- simplify unit test setup with direct instantiation
This commit is contained in:
neon
2026-04-07 07:30:07 -06:00
parent 3c95efe25b
commit ef82cb5d00
8 changed files with 45 additions and 75 deletions
+3
View File
@@ -37,6 +37,9 @@ RUN mkdir -p /deploy/migrations && cp -r /app/server/src/db/migrations/. /deploy
FROM ${NODE_IMAGE} AS runtime
WORKDIR /app
RUN apk upgrade --no-cache && \
rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx
ENV NODE_ENV=production
ENV PORT=3000
+3
View File
@@ -62,6 +62,9 @@
]
},
"pnpm": {
"overrides": {
"fastify": "^5.8.5"
},
"onlyBuiltDependencies": [
"@nestjs/core",
"@parcel/watcher",
+3 -3
View File
@@ -39,7 +39,7 @@
"@fastify/helmet": "^13.0.2",
"@fastify/multipart": "^9.4.0",
"@fastify/rate-limit": "^10.3.0",
"@fastify/static": "^9.0.0",
"@fastify/static": "^9.1.0",
"@nestjs/common": "^11.1.18",
"@nestjs/config": "^4.0.3",
"@nestjs/core": "^11.1.18",
@@ -89,11 +89,11 @@
"@nestjs/schematics": "^11.0.10",
"@nestjs/testing": "^11.1.18",
"@swc/cli": "^0.8.1",
"@swc/core": "^1.15.21",
"@swc/core": "^1.15.24",
"@types/bcrypt": "^6.0.0",
"@types/express": "^5.0.6",
"@types/node": "^25.5.2",
"@types/nodemailer": "^6.4.23",
"@types/nodemailer": "^8.0.0",
"@types/passport-jwt": "^4.0.1",
"@types/pg": "^8.18.0",
"@types/supertest": "^7.2.0",
@@ -1,5 +1,4 @@
import { ConflictException, ForbiddenException, NotFoundException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { EmailProviderService } from './email-provider.service';
import { EmailProviderRepository } from './email-provider.repository';
import { EmailEncryptionService } from './email-encryption.service';
@@ -46,38 +45,29 @@ describe('EmailProviderService', () => {
startTls: true,
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
EmailProviderService,
{
provide: EmailProviderRepository,
useValue: {
findAllForUser: vi.fn().mockResolvedValue([mockProvider]),
findById: vi.fn().mockResolvedValue([mockProvider]),
insert: vi.fn().mockResolvedValue([mockProvider]),
update: vi.fn().mockResolvedValue([mockProvider]),
delete: vi.fn(),
clearDefault: vi.fn(),
setDefault: vi.fn().mockResolvedValue([mockProvider]),
setSharedByOwner: vi.fn().mockResolvedValue([{ ...mockProvider, isShared: true }]),
},
},
{
provide: EmailEncryptionService,
useValue: { encrypt: vi.fn().mockReturnValue('encrypted'), decrypt: vi.fn().mockReturnValue('decrypted') },
},
{
provide: EmailTransportService,
useValue: { buildTransporter: vi.fn(), verifyTransporter: vi.fn() },
},
],
}).compile();
beforeEach(() => {
repo = {
findAllForUser: vi.fn().mockResolvedValue([mockProvider]),
findById: vi.fn().mockResolvedValue([mockProvider]),
insert: vi.fn().mockResolvedValue([mockProvider]),
update: vi.fn().mockResolvedValue([mockProvider]),
delete: vi.fn(),
clearDefault: vi.fn(),
setDefault: vi.fn().mockResolvedValue([mockProvider]),
setSharedByOwner: vi.fn().mockResolvedValue([{ ...mockProvider, isShared: true }]),
} as unknown as EmailProviderRepository;
service = module.get<EmailProviderService>(EmailProviderService);
repo = module.get<EmailProviderRepository>(EmailProviderRepository);
encryption = module.get<EmailEncryptionService>(EmailEncryptionService);
transport = module.get<EmailTransportService>(EmailTransportService);
encryption = {
encrypt: vi.fn().mockReturnValue('encrypted'),
decrypt: vi.fn().mockReturnValue('decrypted'),
} as unknown as EmailEncryptionService;
transport = {
buildTransporter: vi.fn(),
verifyTransporter: vi.fn(),
} as unknown as EmailTransportService;
service = new EmailProviderService(repo, encryption, transport);
});
describe('findAll', () => {
@@ -1,4 +1,3 @@
import { Test, TestingModule } from '@nestjs/testing';
import * as nodemailer from 'nodemailer';
import { EmailTransportService } from './email-transport.service';
@@ -7,12 +6,9 @@ vi.mock('nodemailer');
describe('EmailTransportService', () => {
let service: EmailTransportService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [EmailTransportService],
}).compile();
service = module.get<EmailTransportService>(EmailTransportService);
beforeEach(() => {
vi.clearAllMocks();
service = new EmailTransportService();
});
it('should build transporter with correct config (Port 587)', () => {
@@ -1,4 +1,3 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ProviderConfigurations } from '@projectx/types';
import { ProviderConfigService } from '../../../metadata-preferences/provider-config.service';
@@ -21,21 +20,11 @@ describe('GoodreadsProvider', () => {
comicvine: { enabled: false, apiKey: '' },
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
GoodreadsProvider,
{
provide: ProviderConfigService,
useValue: {
getConfig: vi.fn().mockResolvedValue(mockConfig),
},
},
],
}).compile();
provider = module.get<GoodreadsProvider>(GoodreadsProvider);
providerConfig = module.get<ProviderConfigService>(ProviderConfigService);
beforeEach(() => {
providerConfig = {
getConfig: vi.fn().mockResolvedValue(mockConfig),
} as unknown as ProviderConfigService;
provider = new GoodreadsProvider(providerConfig);
});
afterEach(() => {
@@ -1,4 +1,3 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ProviderConfigurations } from '@projectx/types';
import { ProviderConfigService } from '../../../metadata-preferences/provider-config.service';
@@ -20,21 +19,11 @@ describe('OpenLibraryProvider', () => {
comicvine: { enabled: false, apiKey: '' },
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
OpenLibraryProvider,
{
provide: ProviderConfigService,
useValue: {
getConfig: vi.fn().mockResolvedValue(mockConfig),
},
},
],
}).compile();
provider = module.get<OpenLibraryProvider>(OpenLibraryProvider);
providerConfig = module.get<ProviderConfigService>(ProviderConfigService);
beforeEach(() => {
providerConfig = {
getConfig: vi.fn().mockResolvedValue(mockConfig),
} as unknown as ProviderConfigService;
provider = new OpenLibraryProvider(providerConfig);
});
afterEach(() => {
+1 -1
View File
@@ -11,7 +11,7 @@ export default defineConfig({
test: {
globals: true,
environment: 'node',
pool: 'forks',
pool: 'threads',
include: ['src/**/*.test.ts', 'test/**/*.test.ts'],
passWithNoTests: true,
reporters: process.env.CI ? ['default', 'github-actions'] : ['default'],