High severity7.5NVD Advisory· Published Jul 23, 2024· Updated Apr 15, 2026
CVE-2024-41655
CVE-2024-41655
Description
TF2 Item Format helps users format TF2 items to the community standards. Versions of tf2-item-format since at least 4.2.6 and prior to 5.9.14 are vulnerable to a Regular Expression Denial of Service (ReDoS) attack when parsing crafted user input. This vulnerability can be exploited by an attacker to perform DoS attacks on any service that uses any tf2-item-format to parse user input. Version 5.9.14 contains a fix for the issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
tf2-item-formatnpm | >= 4.2.6, < 5.9.14 | 5.9.14 |
Patches
25cffcc16a926Merge pull request #371 from danocmx/fix/regex
10 files changed · +44 −19
src/parseEconItem/ParsedEcon/getDescriptions/paint.ts+1 −1 modified@@ -5,7 +5,7 @@ import { EconDescription } from '../../../types'; */ export function isPaint(description: EconDescription): boolean { return ( - /^Paint Color: /.test(description.value) && + description.value.startsWith('Paint Color: ') && description.color === '756b5e' ); }
src/parseEconItem/ParsedEcon/getDescriptions/spell.ts+1 −1 modified@@ -7,7 +7,7 @@ import { EconDescription } from '../../../types'; */ export function isSpell(description: EconDescription): boolean { return ( - /^Halloween: .*/.test(description.value) && + description.value.startsWith('Halloween: ') && description.color === '7ea9d1' ); }
src/parseString/Attributes/getQuality/isHaunted.ts+12 −3 modified@@ -6,8 +6,17 @@ export default function (name: string): boolean { ); } +const HAUNTED_EXCEPTIONS = [ + 'Haunted Hat', + 'Haunted Ghosts', + 'Haunted Phantasm', + 'Haunted Metal Scrap', + 'Haunted Kraken', + 'Haunted Forever', + 'Haunted Wick', + 'Haunted Cremation', +]; + function isHauntedException(name: string): boolean { - return /(Haunted Hat)|(Haunted Ghosts)|(Haunted Phantasm)|(Haunted Metal Scrap)|(Haunted Kraken)|(Haunted Forever)|(Haunted Wick)|(Haunted Cremation)/.test( - name - ); + return HAUNTED_EXCEPTIONS.some((exception) => name.includes(exception)); }
src/parseString/Attributes/getQuality/isStrange.ts+9 −3 modified@@ -2,8 +2,14 @@ export default function (name: string): boolean { return name.includes('Strange ') && !isStrangeException(name); } +const STRANGE_EXCEPTIONS = [ + 'Strange Bacon Grease', + 'Strange Filter: ', + 'Strange Count Transfer Tool', + 'Strange Part: ', + 'Strange Cosmetic Part: ', +]; + function isStrangeException(name: string): boolean { - return /(Strange Bacon Grease|Strange Filter: |Strange Count Transfer Tool|Strange Part: |Strange Cosmetic Part: )/.test( - name - ); + return STRANGE_EXCEPTIONS.some((exception) => name.includes(exception)); }
src/parseString/Attributes/getQuality/isVintage.ts+6 −1 modified@@ -6,6 +6,11 @@ export default function (name: string): boolean { ); } +const VINTAGE_EXCEPTIONS = [ + 'Vintage Merryweather', + 'Vintage Tyrolean', +]; + function isVintageException(name: string): boolean { - return /Vintage (Merryweather|Tyrolean)/.test(name); + return VINTAGE_EXCEPTIONS.some((exception) => name.includes(exception)); }
src/parseString/Attributes/getQuality/matchQuality.ts+1 −1 modified@@ -4,7 +4,7 @@ * @return {string} quality */ export default function (name: string): string { - // Does not include strangee and vintage for exception reasons. + // Does not include strange and vintage for exception reasons. const match = name.match(/(Normal|Genuine|Unique|Unusual|Self-Made|Collector's) /) || [];
src/parseString/Attributes/getUsableItem.ts+9 −3 modified@@ -48,17 +48,23 @@ export default function (name: string): Partial<TargetOutputItem> | null { } function isStrangifierChemistrySet(name: string): boolean { - return / Strangifier Chemistry Set/.test(name); + return name.includes(' Strangifier Chemistry Set'); } +const TARGET_EXCEPTIONS = [ + "Killer's Kit", + "Coffin Kit", + "Summer Starter Kit" +]; + function getItemIfTarget(name: string): string | void { - if (/(Killer's Kit|Coffin Kit|Summer Starter Kit)/.test(name)) return; + if (TARGET_EXCEPTIONS.some((exception) => name.includes(exception))) return; // eslint-disable-next-line consistent-return return (name.match(/ (Kit Fabricator|Kit|Strangifier|Unusualifier)/) || [])[1]; } function isChemistrySet(name: string): boolean { - return / Chemistry Set/.test(name); + return name.includes(' Chemistry Set') ; }
src/shared/decomposeName.ts+3 −4 modified@@ -51,10 +51,9 @@ export default function ( } else { const toRemove = getUsableItemToRemove(attributes); - itemName = itemName.replace( - new RegExp(`(( ${toRemove})|(${toRemove} ))`), - '' - ); + itemName = itemName + .replace(` ${toRemove}`, '') + .replace(`${toRemove} `, ''); } } else if (killstreak) itemName = itemName.replace(`${killstreak} `, ''); // Killstreak stat is kept
src/shared/isUniqueHat.ts+1 −1 modified@@ -46,7 +46,7 @@ const EXCEPTIONS = [ * Only happens to hats with unique quality. */ export default function (name: string, attributes?: Attributes): boolean { - if (!/^The /.test(name)) { + if (!name.startsWith('The ')) { return false; }
src/stringify.ts+1 −1 modified@@ -189,7 +189,7 @@ function canAddKillstreak( } function isKillstreakKitOrFabricator(name: string, target?: string): boolean { - return !!(target && (/ Kit/.test(name) || / Fabricator/.test(name))); // This checks for fabricator too. + return !!(target && (name.includes(' Kit') || name.includes(' Fabricator'))); // This checks for fabricator too. } function isUniqueHat(
ce7417bc10e8Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-8h55-q5qq-p685ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-41655ghsaADVISORY
- github.com/danocmx/node-tf2-item-format/commit/5cffcc16a9261d6a937bda72bfe6830e02e31eecnvdWEB
- github.com/danocmx/node-tf2-item-format/releases/tag/v5.9.14nvdWEB
- github.com/danocmx/node-tf2-item-format/security/advisories/GHSA-8h55-q5qq-p685nvdWEB
News mentions
0No linked articles in our index yet.