mirror of
https://github.com/mattermost/mattermost.git
synced 2026-09-01 15:00:08 +08:00
update packages to Fix npm audit vulnerabilities (#35810)
* Fix npm audit vulnerabilities and replace image-webpack-loader with sharp * remove dead webp config and restore SVG optimization via svgoMinify * fix import order * removed unused gif from testing, and add missing peer dependency * make sure only svgs are parsed * scope svgoMinify to src- SVGs via loader rule * remove redundant npm overrides per review feedback --------- Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify that webpack-bundled static image assets are not broken by
|
||||
* the image-minimizer-webpack-plugin (sharp) migration.
|
||||
*
|
||||
* Sharp runs at build time and compresses PNG/JPEG/SVG assets bundled into the
|
||||
* app. If it mis-encodes a file the asset will either 404, return a wrong
|
||||
* content-type, or decode to a zero-width image in the browser.
|
||||
*
|
||||
* This test catches that by reloading the page and asserting:
|
||||
* - no static asset request returns 4xx/5xx
|
||||
* - no <img> in the DOM has naturalWidth === 0 (failed to decode)
|
||||
*/
|
||||
|
||||
test('app loads without any broken image assets on the main channel view', {tag: '@image_assets'}, async ({pw}) => {
|
||||
const {user} = await pw.initSetup();
|
||||
const {page, channelsPage} = await pw.testBrowser.login(user);
|
||||
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Collect image/font load errors on reload so the response listener is
|
||||
// active before any requests fire.
|
||||
const failedImageUrls: string[] = [];
|
||||
page.on('response', (response) => {
|
||||
const url = response.url();
|
||||
const isImage = /\.(png|jpg|jpeg|svg|gif|woff2|woff)(\?|$)/.test(url);
|
||||
if (isImage && response.status() >= 400) {
|
||||
failedImageUrls.push(`${response.status()} ${url}`);
|
||||
}
|
||||
});
|
||||
|
||||
await page.reload();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// * No image/font requests should return 4xx or 5xx
|
||||
expect(failedImageUrls, `Failed asset requests:\n${failedImageUrls.join('\n')}`).toHaveLength(0);
|
||||
|
||||
// * No <img> element should have naturalWidth === 0 (means the file was
|
||||
// served but the browser could not decode it — typical of a sharp corruption)
|
||||
const brokenImages = await page.evaluate(() => {
|
||||
return Array.from(document.querySelectorAll('img'))
|
||||
.filter((img) => img.complete && img.naturalWidth === 0 && Boolean(img.src))
|
||||
.map((img) => img.src);
|
||||
});
|
||||
|
||||
expect(brokenImages, `Broken <img> elements found:\n${brokenImages.join('\n')}`).toHaveLength(0);
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify that the pdfjs cmaps path fix in webpack.config.js works.
|
||||
*
|
||||
* Context: PR #35810 changed the copy-webpack-plugin entry for pdfjs cmaps from
|
||||
* a fragile relative path to one resolved via require.resolve():
|
||||
*
|
||||
* Before: {from: '../node_modules/pdfjs-dist/cmaps', to: 'cmaps'}
|
||||
* After: {from: path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'cmaps'), to: 'cmaps'}
|
||||
*
|
||||
* The old path broke with npm workspace hoisting — pdfjs-dist gets installed at
|
||||
* the root node_modules, not channels/node_modules, so the relative path resolves
|
||||
* to nothing and copy-webpack-plugin silently copies zero files.
|
||||
*
|
||||
* A 404 on identity-h means the cmaps directory was not copied to /static/cmaps/.
|
||||
*/
|
||||
|
||||
test('pdfjs cmaps are copied to /static/cmaps/ and served correctly', {tag: '@pdf_preview'}, async ({pw}) => {
|
||||
const {user} = await pw.initSetup();
|
||||
const {page} = await pw.testBrowser.login(user);
|
||||
|
||||
const baseUrl = new URL(page.url()).origin;
|
||||
|
||||
// # identity-h is a standard CMap that pdfjs requests for non-Latin PDFs.
|
||||
// Its presence confirms copy-webpack-plugin found and copied the cmaps directory.
|
||||
const cmapUrl = `${baseUrl}/static/cmaps/identity-h`;
|
||||
const response = await page.request.get(cmapUrl);
|
||||
|
||||
// * 200 = require.resolve() found the right path and cmaps were copied.
|
||||
// * 404 = the path in webpack.config.js is wrong or copy-webpack-plugin skipped it.
|
||||
expect(
|
||||
response.status(),
|
||||
`CMap not found at ${cmapUrl} — pdfjs cmaps were not copied to /static/cmaps/. ` +
|
||||
'Check the copy-webpack-plugin entry in webpack.config.js.',
|
||||
).toBe(200);
|
||||
|
||||
const body = await response.body();
|
||||
expect(body.length, 'identity-h CMap file must not be empty').toBeGreaterThan(0);
|
||||
});
|
||||
@@ -25,6 +25,7 @@ const config = {
|
||||
'^mattermost-redux/test/(.*)$':
|
||||
'<rootDir>/src/packages/mattermost-redux/test/$1',
|
||||
'^mattermost-redux/(.*)$': '<rootDir>/src/packages/mattermost-redux/src/$1',
|
||||
'^pdfjs-dist/.*': '<rootDir>/src/tests/pdfjs_mock.ts',
|
||||
'^.+\\.(jpg|jpeg|png|apng|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||
'<rootDir>/src/tests/image_url_mock.json',
|
||||
'^.+\\.(css|less|scss)$': 'identity-obj-proxy',
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"katex": "0.16.21",
|
||||
"localforage": "1.10.0",
|
||||
"localforage-observable": "2.1.1",
|
||||
"lodash": "4.17.23",
|
||||
"lodash": "4.18.1",
|
||||
"luxon": "3.6.1",
|
||||
"mark.js": "8.11.1",
|
||||
"marked": "github:mattermost/marked#e4a8785014b26ba9f637c1fdab23e340961c6a03",
|
||||
@@ -59,7 +59,7 @@
|
||||
"monaco-editor": "0.52.2",
|
||||
"monaco-editor-webpack-plugin": "7.1.0",
|
||||
"p-queue": "7.3.0",
|
||||
"pdfjs-dist": "4.4.168",
|
||||
"pdfjs-dist": "4.10.38",
|
||||
"process": "0.11.10",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.2.0",
|
||||
@@ -134,7 +134,7 @@
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/zen-observable": "0.8.7",
|
||||
"babel-plugin-styled-components": "2.1.4",
|
||||
"copy-webpack-plugin": "11.0.0",
|
||||
"copy-webpack-plugin": "14.0.0",
|
||||
"emoji-datasource": "6.1.1",
|
||||
"emoji-datasource-apple": "6.1.1",
|
||||
"emoji-datasource-google": "6.1.1",
|
||||
@@ -143,9 +143,9 @@
|
||||
"html-loader": "5.1.0",
|
||||
"html-webpack-plugin": "5.5.0",
|
||||
"identity-obj-proxy": "3.0.0",
|
||||
"image-webpack-loader": "8.1.0",
|
||||
"imagemin-gifsicle": "7.0.0",
|
||||
"imagemin-mozjpeg": "9.0.0",
|
||||
"image-minimizer-webpack-plugin": "5.0.0",
|
||||
"svgo": "2.8.2",
|
||||
"sharp": "0.34.5",
|
||||
"jest": "30.1.3",
|
||||
"jest-canvas-mock": "2.5.0",
|
||||
"jest-cli": "30.1.3",
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.mjs';
|
||||
import React from 'react';
|
||||
|
||||
// All pdfjs-dist/* imports map to the same file via moduleNameMapper.
|
||||
// Use the imported reference directly — separate jest.mock() calls for different
|
||||
// pdfjs paths resolve to the same key and silently overwrite each other.
|
||||
|
||||
import PDFPreview from 'components/pdf_preview';
|
||||
import type {Props} from 'components/pdf_preview';
|
||||
|
||||
import {render, renderWithContext, waitFor} from 'tests/react_testing_utils';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
const mockGetDocument = jest.fn();
|
||||
|
||||
jest.mock('pdfjs-dist/legacy/build/pdf.mjs', () => ({
|
||||
getDocument: (params: unknown) => mockGetDocument(params),
|
||||
}));
|
||||
|
||||
jest.mock('pdfjs-dist/build/pdf.worker.min.mjs', () => ({}));
|
||||
const mockGetDocument = pdfjsLib.getDocument as jest.Mock;
|
||||
|
||||
describe('component/PDFPreview', () => {
|
||||
const requiredProps: Props = {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// pdfjs-dist 4.x ships ESM-only builds that use import.meta.url, which Jest's
|
||||
// CommonJS runtime cannot parse. This mock replaces all pdfjs-dist imports in
|
||||
// tests so that components which transitively depend on pdf_preview.tsx compile
|
||||
// without requiring the native pdfjs binary pipeline.
|
||||
//
|
||||
// This is intentional: running real pdfjs in jsdom would require mocking canvas,
|
||||
// workers, and CMap fetches anyway — the mock is the right level of abstraction
|
||||
// for unit tests. For pdf_preview.tsx component tests, extend this mock as needed.
|
||||
|
||||
export const GlobalWorkerOptions = {workerSrc: ''};
|
||||
|
||||
export const getDocument = jest.fn(() => ({
|
||||
promise: Promise.resolve({
|
||||
numPages: 1,
|
||||
getPage: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
getViewport: jest.fn(() => ({width: 100, height: 100, scale: 1})),
|
||||
render: jest.fn(() => ({promise: Promise.resolve(), cancel: jest.fn()})),
|
||||
cleanup: jest.fn(),
|
||||
}),
|
||||
),
|
||||
destroy: jest.fn(),
|
||||
}),
|
||||
destroy: jest.fn(),
|
||||
}));
|
||||
|
||||
export const version = '4.10.38';
|
||||
@@ -9,6 +9,7 @@ const url = require('url');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const ExternalTemplateRemotesPlugin = require('external-remotes-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
@@ -106,14 +107,6 @@ var config = {
|
||||
{
|
||||
test: /\.(png|eot|tiff|svg|woff2|woff|ttf|gif|mp3|jpg)$/,
|
||||
type: 'asset/resource',
|
||||
use: [
|
||||
|
||||
// Skip image optimizations during development to speed up build time
|
||||
!DEV && {
|
||||
loader: 'image-webpack-loader',
|
||||
options: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.apng$/,
|
||||
@@ -205,7 +198,7 @@ var config = {
|
||||
{from: 'src/fonts/open-sans-v18-vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic-regular.woff', to: 'fonts'},
|
||||
{from: 'src/fonts/open-sans-v18-vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic-600.woff2', to: 'fonts'},
|
||||
{from: 'src/fonts/open-sans-v18-vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic-600.woff', to: 'fonts'},
|
||||
{from: '../node_modules/pdfjs-dist/cmaps', to: 'cmaps'},
|
||||
{from: path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'cmaps'), to: 'cmaps'},
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -423,6 +416,49 @@ if (DEV) {
|
||||
// Production mode configuration
|
||||
config.mode = 'production';
|
||||
config.devtool = 'source-map';
|
||||
|
||||
// Optimize SVGs from src/ at module-build time so vendor SVG fonts (e.g. font-awesome)
|
||||
// are never passed to svgo. Vendor SVGs get content-hashed names at emit time, making
|
||||
// them indistinguishable from src/ assets in the optimization.minimizer phase.
|
||||
config.module.rules.push({
|
||||
test: /\.svg$/i,
|
||||
include: [path.resolve(__dirname, 'src')],
|
||||
enforce: 'pre',
|
||||
loader: ImageMinimizerPlugin.loader,
|
||||
options: {
|
||||
minimizer: {
|
||||
implementation: ImageMinimizerPlugin.svgoMinify,
|
||||
options: {
|
||||
encodeOptions: {
|
||||
multipass: true,
|
||||
plugins: ['preset-default'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Optimize images in production builds.
|
||||
// GIFs are excluded from sharp: animated GIFs (e.g. Customize-Your-Experience.gif)
|
||||
// lose frames or grow in size when re-encoded by sharp.
|
||||
config.optimization = {
|
||||
...config.optimization,
|
||||
minimizer: [
|
||||
'...',
|
||||
new ImageMinimizerPlugin({
|
||||
minimizer: {
|
||||
implementation: ImageMinimizerPlugin.sharpMinify,
|
||||
filter: (source, sourcePath) => !sourcePath.endsWith('.gif'),
|
||||
options: {
|
||||
encodeOptions: {
|
||||
jpeg: {mozjpeg: true},
|
||||
png: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const env = {};
|
||||
|
||||
Generated
+8492
-6196
File diff suppressed because it is too large
Load Diff
+22
-4
@@ -47,13 +47,13 @@
|
||||
"eslint-plugin-react": "7.34.0",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"mini-css-extract-plugin": "2.7.5",
|
||||
"patch-package": "8.0.0",
|
||||
"patch-package": "8.0.1",
|
||||
"sass": "1.80.5",
|
||||
"sass-loader": "16.0.2",
|
||||
"strip-ansi": "7.1.0",
|
||||
"style-loader": "4.0.0",
|
||||
"typescript-eslint-language-service": "5.0.5",
|
||||
"webpack": "5.103.0",
|
||||
"webpack": "5.106.2",
|
||||
"webpack-cli": "6.0.1",
|
||||
"webpack-dev-server": "5.2.2"
|
||||
},
|
||||
@@ -62,7 +62,7 @@
|
||||
"jest": "30.1.3"
|
||||
},
|
||||
"@giphy/react-components": {
|
||||
"styled-components": "5.3.6"
|
||||
"styled-components": "5.3.7"
|
||||
},
|
||||
"@types/redux-mock-store": {
|
||||
"redux": "5.0.1"
|
||||
@@ -82,7 +82,25 @@
|
||||
},
|
||||
"redux-persist": {
|
||||
"redux": "5.0.1"
|
||||
}
|
||||
},
|
||||
"ajv@>=7.0.0-alpha.0 <8.18.0": "8.20.0",
|
||||
"bn.js@>=5.0.0 <5.2.3": "5.2.3",
|
||||
"bn.js@<4.12.3": "4.12.3",
|
||||
"brace-expansion@<1.1.13": "1.1.14",
|
||||
"brace-expansion@>=2.0.0 <2.0.3": "2.1.0",
|
||||
"fast-xml-parser@>=4.0.0-beta.0 <4.5.4": "4.5.5",
|
||||
"flatted": "3.4.2",
|
||||
"follow-redirects": "1.16.0",
|
||||
"immutable": "4.3.8",
|
||||
"js-yaml": "4.1.1",
|
||||
"node-forge": "1.4.0",
|
||||
"path-to-regexp@<0.1.13": "0.1.13",
|
||||
"picomatch@<2.3.2": "2.3.2",
|
||||
"picomatch@>=4.0.0 <4.0.4": "4.0.4",
|
||||
"postcss": "8.5.13",
|
||||
"rollup@<2.80.0": "2.80.0",
|
||||
"yaml@<1.10.3": "1.10.3",
|
||||
"yaml@>=2.0.0 <2.8.3": "2.8.4"
|
||||
},
|
||||
"workspaces": [
|
||||
"channels",
|
||||
|
||||
Reference in New Issue
Block a user