Skip to content

Update depedencies to fix known security vulnerabiltiies #165

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

malor
Copy link
Member

@malor malor commented Jun 21, 2025

  • This quickly snowballs into updating the React version, as some of the dependent libraries require the latest one. Updating React requires dropping Enzyme in favour of React Testing Library.

  • The other major chunk of vulnerabilities is in webpack

malor added 3 commits June 21, 2025 08:09
Enzyme does not support React >= 17, and we want to upgrade to that
version to resolve known security vulnerabilities in the dependant
libraries.

Apparentely, React Testing Library is the recommended replacement
(https://testing-library.com/docs/react-testing-library/migrate-from-enzyme).
* This quickly snowballs to updating the React version as some of the
  dependant libraries (like react-tag-input) only support the latest one.

* The other major chunk of vulnerabilities was in webpack.

* Tests were migrated from Enzyme to React Testing Library beforehand
Apparantely, React Router 7 is going to bring some changes, and people
are encouraged to opt int to those early.
// manipulate underlying UI library (it can be used not only with React).
// Since we use React 16, we need to set a proper adapter.
configure({ adapter: new Adapter() })
// React Testing Library setup - jest-dom provides custom matchers for DOM elements
Copy link
Member Author

Choose a reason for hiding this comment

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

Looks like we don't need a custom test setup anymore?

Copy link
Member

Choose a reason for hiding this comment

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

Looks like it.

@@ -3,7 +3,7 @@ const process = require('process')

const webpack = require('webpack')

const CleanWebpackPlugin = require('clean-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
Copy link
Member Author

Choose a reason for hiding this comment

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

What is this change?

Copy link
Member

Choose a reason for hiding this comment

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

Unpacking, or deconstruction. Most likely require('clean-webpack-plugin') used to return just one class, and now it returns a mapping (object in js) where CleanWebpackPlugin is just one of its keys.

The syntax with { CleanWebpackPlugin } just allows to bind the CleanWebpackPlugin key of the require('clean-webpack-plugin') object to the CleanWebpackPlugin local constant.

@@ -168,29 +168,24 @@ module.exports = () => {
loader: 'css-loader',
options: {
modules: false,
minimize: true,
Copy link
Member Author

Choose a reason for hiding this comment

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

Why did Claude decide to drop this?

// Enable source maps if they are specified in devtool
// option. God-Knows-Why css-loader doesn't check devtool
// value in order to initialize its sourceMap value, hence
// this line.
sourceMap: true,
url: true,
Copy link
Member Author

Choose a reason for hiding this comment

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

Why is this needed now?

{
test: /\.(png|svg|jpg|woff|svg|ttf|woff2|eot)$/,
Copy link
Member Author

Choose a reason for hiding this comment

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

Why is svg dropped?

Copy link
Member

Choose a reason for hiding this comment

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

FWIW, it seems svg was duplicated.


node: {
net: 'empty',
fallback: {
Copy link
Member Author

Choose a reason for hiding this comment

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

What's this for?

@@ -1,6 +1,6 @@
@font-face
font-family: "IconFont"
src: url("../assets/fonts/icon-font.woff") format("woff")
src: url("../../assets/fonts/icon-font.woff") format("woff")
Copy link
Member Author

Choose a reason for hiding this comment

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

It's only one level up?

Copy link
Member

Choose a reason for hiding this comment

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

🤷

<Route path="/sign-in" element={<SignIn />} />
{/* Routes renders the first matched route, so we need to keep this
one last as the regexp below matches the paths of other pages */}
<Route path="/:id" element={<Snippet />} />
Copy link
Member Author

Choose a reason for hiding this comment

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

([a-zA-Z0-9]+) should have been preserved?

Copy link
Member

Choose a reason for hiding this comment

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

Probably. But I don't think it's important. Worst case we will stop showing 404 on something that doesn't look like a legit snippet ID.

Copy link
Member

Choose a reason for hiding this comment

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

Probably. But I don't think it's important. Worst case we will stop showing 404 on something that doesn't look like a legit snippet ID.

},
"scripts": {
"lint": "eslint --ignore-path .gitignore --ext ts .",
"lint": "eslint --ext ts,tsx src/",
Copy link
Member Author

Choose a reason for hiding this comment

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

This no longer lints the tests?


import Spinner from '../../../src/components/common/Spinner'

// Mock the SVG import to prevent test failures
Copy link
Member Author

Choose a reason for hiding this comment

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

What is this?

Copy link
Member

Choose a reason for hiding this comment

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

As far as I remember, webpack patches the import statement so it can track what was used and produce the final bundle that contains what was used only. In addition to that, it extends import behavior in not compatible way -- it allows importing assets, so it can do the same thing for icons and images and literally anything else.

@malor malor requested review from ikalnytskyi and lotrien June 21, 2025 08:40
@malor
Copy link
Member Author

malor commented Jun 21, 2025

@lotrien @ikalnytskyi Check out this magnificent creation by Claude :)

"joi": "^17.13.3",
"parse-link-header": "^2.0.0",
"querystring-es3": "^0.2.1",
"react": "^18.3.1",
Copy link
Member

Choose a reason for hiding this comment

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

Interesting, why it wasn't updated to the latest stable version? 🤔


import Spinner from '../../../src/components/common/Spinner'

// Mock the SVG import to prevent test failures
Copy link
Member

Choose a reason for hiding this comment

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

As far as I remember, webpack patches the import statement so it can track what was used and produce the final bundle that contains what was used only. In addition to that, it extends import behavior in not compatible way -- it allows importing assets, so it can do the same thing for icons and images and literally anything else.

const spinnerWrapper = container.querySelector('.spinner')

expect(spinnerWrapper).toBeInTheDocument()
expect(spinnerWrapper).toHaveClass('spinner')
Copy link
Member

Choose a reason for hiding this comment

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

nit: A silly check because we already query by a class name (see L15).

const { container } = render(<Title title={title} />)
const titleElement = container.querySelector('.title')

expect(titleElement).toHaveClass('title')
Copy link
Member

Choose a reason for hiding this comment

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

Ditto.

// manipulate underlying UI library (it can be used not only with React).
// Since we use React 16, we need to set a proper adapter.
configure({ adapter: new Adapter() })
// React Testing Library setup - jest-dom provides custom matchers for DOM elements
Copy link
Member

Choose a reason for hiding this comment

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

Looks like it.


// Each time we change something, a new version of bundled assets is
// produced. Since we use hash in filenames in order to invalidate cache
// on change, we end up having multiple outdated copies in output
// directory. Let's cleanup it before produce a fresh build.
new CleanWebpackPlugin([path.resolve(__dirname, 'dist')]),
Copy link
Member

Choose a reason for hiding this comment

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

It seems it defaults now to output.path of the main webpack config which points to the same directory.

@@ -1,6 +1,6 @@
@font-face
font-family: "IconFont"
src: url("../assets/fonts/icon-font.woff") format("woff")
src: url("../../assets/fonts/icon-font.woff") format("woff")
Copy link
Member

Choose a reason for hiding this comment

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

🤷

@@ -7,17 +7,17 @@ const Sidebar: FC = () => (
<nav className="sidebar">
<ul className="sidebar-list">
<li className="sidebar-item">
<NavLink exact to="/" activeClassName="active" title="New Snippet">
<NavLink to="/" className={({ isActive }) => isActive ? "active" : ""} title="New Snippet" end>
Copy link
Member

Choose a reason for hiding this comment

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

Since our navlinks are always active, do we even need this change?

Also, what's that "end" at the end?

<Route path="/sign-in" element={<SignIn />} />
{/* Routes renders the first matched route, so we need to keep this
one last as the regexp below matches the paths of other pages */}
<Route path="/:id" element={<Snippet />} />
Copy link
Member

Choose a reason for hiding this comment

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

Probably. But I don't think it's important. Worst case we will stop showing 404 on something that doesn't look like a legit snippet ID.

<Route path="/sign-in" element={<SignIn />} />
{/* Routes renders the first matched route, so we need to keep this
one last as the regexp below matches the paths of other pages */}
<Route path="/:id" element={<Snippet />} />
Copy link
Member

Choose a reason for hiding this comment

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

Probably. But I don't think it's important. Worst case we will stop showing 404 on something that doesn't look like a legit snippet ID.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants