Skip to content

Commit

Permalink
Fix unsafe regexp
Browse files Browse the repository at this point in the history
  • Loading branch information
ai committed Apr 8, 2021
1 parent 61be47f commit c091916
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 115 deletions.
242 changes: 137 additions & 105 deletions index.js
Expand Up @@ -614,6 +614,68 @@ browserslist.coverage = function (browsers, stats) {
}, 0)
}

function nodeQuery (context, version) {
var nodeReleases = jsReleases.filter(function (i) {
return i.name === 'nodejs'
})
var matched = nodeReleases.filter(function (i) {
return isVersionsMatch(i.version, version)
})
if (matched.length === 0) {
if (context.ignoreUnknownVersions) {
return []
} else {
throw new BrowserslistError('Unknown version ' + version + ' of Node.js')
}
}
return ['node ' + matched[matched.length - 1].version]
}

function sinceQuery (context, year, month, date) {
year = parseInt(year)
month = parseInt(month || '01') - 1
date = parseInt(date || '01')
return filterByYear(Date.UTC(year, month, date, 0, 0, 0), context)
}

function coverQuery (context, coverage, statMode) {
coverage = parseFloat(coverage)
var usage = browserslist.usage.global
if (statMode) {
if (statMode.match(/^my\s+stats$/)) {
if (!context.customUsage) {
throw new BrowserslistError(
'Custom usage statistics was not provided'
)
}
usage = context.customUsage
} else {
var place
if (statMode.length === 2) {
place = statMode.toUpperCase()
} else {
place = statMode.toLowerCase()
}
env.loadCountry(browserslist.usage, place, browserslist.data)
usage = browserslist.usage[place]
}
}
var versions = Object.keys(usage).sort(function (a, b) {
return usage[b] - usage[a]
})
var coveraged = 0
var result = []
var version
for (var i = 0; i <= versions.length; i++) {
version = versions[i]
if (usage[version] === 0) break
coveraged += usage[version]
result.push(version)
if (coveraged >= coverage) break
}
return result
}

var QUERIES = [
{
regexp: /^last\s+(\d+)\s+major\s+versions?$/i,
Expand Down Expand Up @@ -669,9 +731,11 @@ var QUERIES = [
{
regexp: /^last\s+(\d+)\s+electron\s+versions?$/i,
select: function (context, versions) {
return Object.keys(e2c).slice(-versions).map(function (i) {
return 'chrome ' + e2c[i]
})
return Object.keys(e2c)
.slice(-versions)
.map(function (i) {
return 'chrome ' + e2c[i]
})
}
},
{
Expand Down Expand Up @@ -709,9 +773,11 @@ var QUERIES = [
regexp: /^unreleased\s+(\w+)\s+versions?$/i,
select: function (context, name) {
var data = checkName(name, context)
return data.versions.filter(function (v) {
return data.released.indexOf(v) === -1
}).map(nameMapper(data.name))
return data.versions
.filter(function (v) {
return data.released.indexOf(v) === -1
})
.map(nameMapper(data.name))
}
},
{
Expand All @@ -721,13 +787,16 @@ var QUERIES = [
}
},
{
regexp: /^since (\d+)(?:-(\d+))?(?:-(\d+))?$/i,
select: function (context, year, month, date) {
year = parseInt(year)
month = parseInt(month || '01') - 1
date = parseInt(date || '01')
return filterByYear(Date.UTC(year, month, date, 0, 0, 0), context)
}
regexp: /^since (\d+)$/i,
select: sinceQuery
},
{
regexp: /^since (\d+)-(\d+)$/i,
select: sinceQuery
},
{
regexp: /^since (\d+)-(\d+)-(\d+)$/i,
select: sinceQuery
},
{
regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%$/,
Expand Down Expand Up @@ -849,45 +918,12 @@ var QUERIES = [
}
},
{
regexp: /^cover\s+(\d*\.?\d+)%(\s+in\s+(my\s+stats|(alt-)?\w\w))?$/,
select: function (context, coverage, statMode) {
coverage = parseFloat(coverage)
var usage = browserslist.usage.global
if (statMode) {
if (statMode.match(/^\s+in\s+my\s+stats$/)) {
if (!context.customUsage) {
throw new BrowserslistError(
'Custom usage statistics was not provided'
)
}
usage = context.customUsage
} else {
var match = statMode.match(/\s+in\s+((alt-)?\w\w)/)
var place = match[1]
if (place.length === 2) {
place = place.toUpperCase()
} else {
place = place.toLowerCase()
}
env.loadCountry(browserslist.usage, place, browserslist.data)
usage = browserslist.usage[place]
}
}
var versions = Object.keys(usage).sort(function (a, b) {
return usage[b] - usage[a]
})
var coveraged = 0
var result = []
var version
for (var i = 0; i <= versions.length; i++) {
version = versions[i]
if (usage[version] === 0) break
coveraged += usage[version]
result.push(version)
if (coveraged >= coverage) break
}
return result
}
regexp: /^cover\s+(\d*\.?\d+)%$/,
select: coverQuery
},
{
regexp: /^cover\s+(\d*\.?\d+)%\s+in\s+(my\s+stats|(alt-)?\w\w)$/,
select: coverQuery
},
{
regexp: /^supports\s+([\w-]+)$/,
Expand Down Expand Up @@ -916,31 +952,26 @@ var QUERIES = [
}
from = parseFloat(from)
to = parseFloat(to)
return Object.keys(e2c).filter(function (i) {
var parsed = parseFloat(i)
return parsed >= from && parsed <= to
}).map(function (i) {
return 'chrome ' + e2c[i]
})
return Object.keys(e2c)
.filter(function (i) {
var parsed = parseFloat(i)
return parsed >= from && parsed <= to
})
.map(function (i) {
return 'chrome ' + e2c[i]
})
}
},
{
regexp: /^node\s+([\d.]+)\s*-\s*([\d.]+)$/i,
select: function (context, from, to) {
var nodeVersions = jsReleases.filter(function (i) {
return i.name === 'nodejs'
}).map(function (i) {
return i.version
})
var semverRegExp = /^(0|[1-9]\d*)(\.(0|[1-9]\d*)){0,2}$/
if (!semverRegExp.test(from)) {
throw new BrowserslistError(
'Unknown version ' + from + ' of Node.js')
}
if (!semverRegExp.test(to)) {
throw new BrowserslistError(
'Unknown version ' + to + ' of Node.js')
}
var nodeVersions = jsReleases
.filter(function (i) {
return i.name === 'nodejs'
})
.map(function (i) {
return i.version
})
return nodeVersions
.filter(semverFilterLoose('>=', from))
.filter(semverFilterLoose('<=', to))
Expand Down Expand Up @@ -976,11 +1007,13 @@ var QUERIES = [
{
regexp: /^node\s*(>=?|<=?)\s*([\d.]+)$/i,
select: function (context, sign, version) {
var nodeVersions = jsReleases.filter(function (i) {
return i.name === 'nodejs'
}).map(function (i) {
return i.version
})
var nodeVersions = jsReleases
.filter(function (i) {
return i.name === 'nodejs'
})
.map(function (i) {
return i.version
})
return nodeVersions
.filter(generateSemverFilter(sign, version))
.map(function (v) {
Expand Down Expand Up @@ -1022,30 +1055,23 @@ var QUERIES = [
var chrome = e2c[versionToUse]
if (!chrome) {
throw new BrowserslistError(
'Unknown version ' + version + ' of electron')
'Unknown version ' + version + ' of electron'
)
}
return ['chrome ' + chrome]
}
},
{
regexp: /^node\s+(\d+(\.\d+)?(\.\d+)?)$/i,
select: function (context, version) {
var nodeReleases = jsReleases.filter(function (i) {
return i.name === 'nodejs'
})
var matched = nodeReleases.filter(function (i) {
return isVersionsMatch(i.version, version)
})
if (matched.length === 0) {
if (context.ignoreUnknownVersions) {
return []
} else {
throw new BrowserslistError(
'Unknown version ' + version + ' of Node.js')
}
}
return ['node ' + matched[matched.length - 1].version]
}
regexp: /^node\s+(\d+)$/i,
select: nodeQuery
},
{
regexp: /^node\s+(\d+\.\d+)$/i,
select: nodeQuery
},
{
regexp: /^node\s+(\d+\.\d+\.\d+)$/i,
select: nodeQuery
},
{
regexp: /^current\s+node$/i,
Expand All @@ -1057,13 +1083,17 @@ var QUERIES = [
regexp: /^maintained\s+node\s+versions$/i,
select: function (context) {
var now = Date.now()
var queries = Object.keys(jsEOL).filter(function (key) {
return now < Date.parse(jsEOL[key].end) &&
now > Date.parse(jsEOL[key].start) &&
isEolReleased(key)
}).map(function (key) {
return 'node ' + key.slice(1)
})
var queries = Object.keys(jsEOL)
.filter(function (key) {
return (
now < Date.parse(jsEOL[key].end) &&
now > Date.parse(jsEOL[key].start) &&
isEolReleased(key)
)
})
.map(function (key) {
return 'node ' + key.slice(1)
})
return resolve(queries, context)
}
},
Expand Down Expand Up @@ -1100,7 +1130,8 @@ var QUERIES = [
return []
} else {
throw new BrowserslistError(
'Unknown version ' + version + ' of ' + name)
'Unknown version ' + version + ' of ' + name
)
}
}
return [data.name + ' ' + version]
Expand Down Expand Up @@ -1142,7 +1173,8 @@ var QUERIES = [
select: function (context, name) {
if (byName(name, context)) {
throw new BrowserslistError(
'Specify versions in Browserslist query for browser ' + name)
'Specify versions in Browserslist query for browser ' + name
)
} else {
throw unknownQuery(name)
}
Expand Down
11 changes: 9 additions & 2 deletions package.json
Expand Up @@ -94,9 +94,16 @@
"eslintConfig": {
"extends": "@logux/eslint-config/browser",
"rules": {
"security/detect-unsafe-regex": "off",
"global-require": "off"
}
},
"overrides": [
{
"files": "test/**/*",
"rules": {
"security/detect-unsafe-regex": "off"
}
}
]
},
"eslintIgnore": [
"test/fixtures"
Expand Down
10 changes: 2 additions & 8 deletions test/node.test.ts
Expand Up @@ -25,14 +25,8 @@ it('throws on malformed Node.js version', () => {
browserslist('node 8.01')
}).toThrow(/Unknown/)
expect(() => {
browserslist('node 6 - 8.a')
}).toThrow(/Unknown/)
expect(() => {
browserslist('node 6.6.6.6 - 8')
}).toThrow(/Unknown/)
expect(() => {
browserslist('node 6 - 8.01')
}).toThrow(/Unknown/)
browserslist("node 6 - 8.a");
}).toThrow(/Unknown/);
})

it('return empty array on unknown Node.js version with special flag', () => {
Expand Down

0 comments on commit c091916

Please sign in to comment.