npq 3.11.2 → 3.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/npq-hero.js +5 -3
- package/bin/npq.js +5 -3
- package/lib/helpers/cliSpinner.js +2 -0
- package/lib/helpers/cliSupportHandler.js +0 -3
- package/lib/helpers/packageRepoUtils.js +1 -1
- package/lib/helpers/promiseThrottler.js +2 -0
- package/lib/helpers/reportResults.js +4 -1
- package/lib/helpers/sourcePackages.js +2 -0
- package/lib/marshall.js +0 -1
- package/lib/marshalls/age.marshall.js +35 -38
- package/lib/marshalls/author.marshall.js +1 -1
- package/lib/marshalls/baseMarshall.js +26 -0
- package/lib/marshalls/constants.js +2 -0
- package/lib/marshalls/deprecation.marshall.js +26 -21
- package/lib/marshalls/index.js +0 -1
- package/lib/marshalls/newbin.marshall.js +6 -3
- package/lib/marshalls/provenance.marshall.js +2 -1
- package/lib/marshalls/repo.marshall.js +1 -1
- package/lib/marshalls/scripts.marshall.js +36 -30
- package/lib/marshalls/signatures.marshall.js +0 -1
- package/lib/marshalls/snyk.marshall.js +1 -1
- package/lib/marshalls/typosquatting.marshall.js +0 -2
- package/lib/marshalls/version-maturity.marshall.js +39 -44
- package/lib/packageManager.js +0 -1
- package/package.json +9 -113
- package/scripts/build.js +2 -0
- package/scripts/postinstall.js +2 -0
- package/scripts/preuninstall.js +2 -0
- package/scripts/scriptHelpers.js +2 -0
package/bin/npq-hero.js
CHANGED
|
@@ -71,7 +71,6 @@ marshall
|
|
|
71
71
|
})
|
|
72
72
|
.then((result) => {
|
|
73
73
|
if (result && result.countErrors > 0) {
|
|
74
|
-
// eslint-disable-next-line no-console
|
|
75
74
|
console.log()
|
|
76
75
|
return cliPrompt.prompt({
|
|
77
76
|
name: 'install',
|
|
@@ -80,7 +79,6 @@ marshall
|
|
|
80
79
|
})
|
|
81
80
|
} else {
|
|
82
81
|
if (result && result.countWarnings > 0) {
|
|
83
|
-
// eslint-disable-next-line no-console
|
|
84
82
|
console.log()
|
|
85
83
|
return cliPrompt.autoContinue({
|
|
86
84
|
name: 'install',
|
|
@@ -93,7 +91,11 @@ marshall
|
|
|
93
91
|
return { install: true }
|
|
94
92
|
})
|
|
95
93
|
.then((status) => {
|
|
96
|
-
if (
|
|
94
|
+
if (
|
|
95
|
+
status &&
|
|
96
|
+
Object.prototype.hasOwnProperty.call(status, 'install') &&
|
|
97
|
+
status.install === true
|
|
98
|
+
) {
|
|
97
99
|
pkgMgr.process(PACKAGE_MANAGER_TOOL)
|
|
98
100
|
}
|
|
99
101
|
})
|
package/bin/npq.js
CHANGED
|
@@ -97,7 +97,6 @@ Promise.resolve()
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
if (result && result.countErrors > 0) {
|
|
100
|
-
// eslint-disable-next-line no-console
|
|
101
100
|
console.log()
|
|
102
101
|
return cliPrompt.prompt({
|
|
103
102
|
name: 'install',
|
|
@@ -106,7 +105,6 @@ Promise.resolve()
|
|
|
106
105
|
})
|
|
107
106
|
} else {
|
|
108
107
|
if (result && result.countWarnings > 0) {
|
|
109
|
-
// eslint-disable-next-line no-console
|
|
110
108
|
console.log()
|
|
111
109
|
return cliPrompt.autoContinue({
|
|
112
110
|
name: 'install',
|
|
@@ -119,7 +117,11 @@ Promise.resolve()
|
|
|
119
117
|
return { install: true }
|
|
120
118
|
})
|
|
121
119
|
.then((status) => {
|
|
122
|
-
if (
|
|
120
|
+
if (
|
|
121
|
+
status &&
|
|
122
|
+
Object.prototype.hasOwnProperty.call(status, 'install') &&
|
|
123
|
+
status.install === true
|
|
124
|
+
) {
|
|
123
125
|
pkgMgr.process(cliArgs.packageManager)
|
|
124
126
|
}
|
|
125
127
|
})
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const { styleText } = require('node:util')
|
|
4
4
|
|
|
5
|
-
// eslint-disable-next-line security/detect-child-process
|
|
6
5
|
const childProcess = require('child_process')
|
|
7
6
|
const semver = require('semver')
|
|
8
7
|
|
|
@@ -20,10 +19,8 @@ function isEnvSupport() {
|
|
|
20
19
|
|
|
21
20
|
function noSupportError(failFast) {
|
|
22
21
|
if (isInteractiveTerminal()) {
|
|
23
|
-
// eslint-disable-next-line no-console
|
|
24
22
|
console.error(styleText('red', 'error:'), 'npq suppressed due to old node version')
|
|
25
23
|
} else {
|
|
26
|
-
// eslint-disable-next-line no-console
|
|
27
24
|
console.error('error: npq suppressed due to old node version')
|
|
28
25
|
}
|
|
29
26
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
1
3
|
const { styleText } = require('node:util')
|
|
2
4
|
const { marshallCategories } = require('../marshalls/constants')
|
|
3
5
|
const { isInteractiveTerminal } = require('./cliSupportHandler')
|
|
@@ -12,7 +14,7 @@ function getTerminalWidth() {
|
|
|
12
14
|
const [columns] = process.stdout.getWindowSize()
|
|
13
15
|
return columns
|
|
14
16
|
}
|
|
15
|
-
} catch
|
|
17
|
+
} catch {
|
|
16
18
|
// Fallback if terminal size detection fails
|
|
17
19
|
}
|
|
18
20
|
return 80 // Default fallback width
|
|
@@ -25,6 +27,7 @@ function getTerminalWidth() {
|
|
|
25
27
|
*/
|
|
26
28
|
function getDisplayLength(str) {
|
|
27
29
|
// Remove ANSI escape sequences to get actual character count
|
|
30
|
+
// eslint-disable-next-line no-control-regex
|
|
28
31
|
return str.replace(/\u001b\[[0-9;]*m/g, '').length
|
|
29
32
|
}
|
|
30
33
|
|
package/lib/marshall.js
CHANGED
|
@@ -19,50 +19,47 @@ class Marshall extends BaseMarshall {
|
|
|
19
19
|
return 'Checking package maturity'
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
validate(pkg) {
|
|
23
|
-
|
|
24
|
-
let ageDateDiff = null
|
|
25
|
-
return this.packageRepoUtils
|
|
26
|
-
.getPackageInfo(pkg.packageName)
|
|
27
|
-
.then((data) => {
|
|
28
|
-
if (data && data.time && data.time.created) {
|
|
29
|
-
packageData = data
|
|
30
|
-
const pkgCreatedDate = data.time.created
|
|
31
|
-
const dateDiff = Date.now() - Date.parse(pkgCreatedDate)
|
|
22
|
+
async validate(pkg) {
|
|
23
|
+
const data = await this.packageRepoUtils.getPackageInfo(pkg.packageName)
|
|
32
24
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
`Detected a newly published package (created < ${PACKAGE_AGE_THRESHOLD} days) act carefully`
|
|
37
|
-
)
|
|
38
|
-
}
|
|
25
|
+
if (!data || !data.time || !data.time.created) {
|
|
26
|
+
throw new Warning('Could not determine package age')
|
|
27
|
+
}
|
|
39
28
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
throw new Warning('Could not determine package age')
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
.then((pkg) => {
|
|
46
|
-
return this.packageRepoUtils.getSemVer(pkg.packageName, pkg.packageVersion)
|
|
47
|
-
})
|
|
48
|
-
.then((versionResolved) => {
|
|
49
|
-
const versionReleaseDate = packageData.time[versionResolved]
|
|
50
|
-
const versionDateDiff = new Date() - new Date(versionReleaseDate)
|
|
29
|
+
const pkgCreatedDate = data.time.created
|
|
30
|
+
const dateDiff = Date.now() - Date.parse(pkgCreatedDate)
|
|
51
31
|
|
|
52
|
-
|
|
32
|
+
if (dateDiff < PACKAGE_AGE_THRESHOLD) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Detected a newly published package (created < ${PACKAGE_AGE_THRESHOLD} days) act carefully`
|
|
35
|
+
)
|
|
36
|
+
}
|
|
53
37
|
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
const packageVersion = await this.resolvePackageVersion(
|
|
39
|
+
pkg.packageName,
|
|
40
|
+
pkg.packageVersion,
|
|
41
|
+
data
|
|
42
|
+
)
|
|
56
43
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
44
|
+
if (!packageVersion || !data.time[packageVersion]) {
|
|
45
|
+
throw new Warning('Could not determine package version release date')
|
|
46
|
+
}
|
|
61
47
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
48
|
+
const versionReleaseDate = data.time[packageVersion]
|
|
49
|
+
const versionDateDiff = new Date() - new Date(versionReleaseDate)
|
|
50
|
+
const versionDateDiffInDays = Math.round(versionDateDiff / (1000 * 60 * 60 * 24))
|
|
51
|
+
|
|
52
|
+
let timeAgoText = 'days'
|
|
53
|
+
let timeAgoNumber = versionDateDiffInDays
|
|
54
|
+
|
|
55
|
+
if (versionDateDiffInDays >= 365) {
|
|
56
|
+
timeAgoText = 'years'
|
|
57
|
+
timeAgoNumber = Math.floor(versionDateDiffInDays / 365)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (versionDateDiffInDays >= PACKAGE_AGE_UNMAINTAINED_RISK) {
|
|
61
|
+
throw new Warning(`Detected an old package (created ${timeAgoNumber} ${timeAgoText} ago)`)
|
|
62
|
+
}
|
|
66
63
|
}
|
|
67
64
|
}
|
|
68
65
|
|
|
@@ -48,7 +48,7 @@ class Marshall extends BaseMarshall {
|
|
|
48
48
|
|
|
49
49
|
// Agree with Colin on keeping email regex simple: https://colinhacks.com/essays/reasonable-email-regex
|
|
50
50
|
const emailRegex =
|
|
51
|
-
/^(?!\.)(?!.*\.\.)([a-z0-9_'
|
|
51
|
+
/^(?!\.)(?!.*\.\.)([a-z0-9_'+\-.]*)[a-z0-9_'+-]@([a-z0-9][a-z0-9-]*\.)+[a-z]{2,}$/i
|
|
52
52
|
if (!emailRegex.test(npmUser.email)) {
|
|
53
53
|
throw new Error('The publishing user has no valid email address')
|
|
54
54
|
}
|
|
@@ -67,6 +67,32 @@ class BaseMarshall {
|
|
|
67
67
|
message: msg.message
|
|
68
68
|
})
|
|
69
69
|
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Resolves a package version string to an actual semver version
|
|
73
|
+
* Handles dist-tags (latest, beta, etc.) and direct semver versions
|
|
74
|
+
* @param {string} packageName - The package name
|
|
75
|
+
* @param {string} versionSpec - The version specification (could be dist-tag or semver)
|
|
76
|
+
* @param {Object} packageData - Optional package data to avoid re-fetching
|
|
77
|
+
* @returns {Promise<string|null>} - The resolved semver version or null if not found
|
|
78
|
+
*/
|
|
79
|
+
async resolvePackageVersion(packageName, versionSpec, packageData = null) {
|
|
80
|
+
// Get package data if not provided
|
|
81
|
+
const data = packageData || (await this.packageRepoUtils.getPackageInfo(packageName))
|
|
82
|
+
|
|
83
|
+
if (!data || !data['dist-tags']) {
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check if versionSpec is a dist-tag
|
|
88
|
+
if (Object.prototype.hasOwnProperty.call(data['dist-tags'], versionSpec)) {
|
|
89
|
+
return data['dist-tags'][versionSpec]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// If not a dist-tag, try parsing as semver
|
|
93
|
+
const parsed = this.packageRepoUtils.parsePackageVersion(versionSpec)
|
|
94
|
+
return parsed ? parsed.version : null
|
|
95
|
+
}
|
|
70
96
|
}
|
|
71
97
|
|
|
72
98
|
module.exports = BaseMarshall
|
|
@@ -16,27 +16,32 @@ class Marshall extends BaseMarshall {
|
|
|
16
16
|
return 'Checking package for deprecation flag'
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
validate(pkg) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
19
|
+
async validate(pkg) {
|
|
20
|
+
const data = await this.packageRepoUtils.getPackageInfo(pkg.packageName)
|
|
21
|
+
|
|
22
|
+
if (!data) {
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const packageVersion = await this.resolvePackageVersion(
|
|
27
|
+
pkg.packageName,
|
|
28
|
+
pkg.packageVersion,
|
|
29
|
+
data
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if (!packageVersion) {
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const packageDeprecated =
|
|
37
|
+
data &&
|
|
38
|
+
data.versions &&
|
|
39
|
+
data.versions[packageVersion] &&
|
|
40
|
+
data.versions[packageVersion].deprecated
|
|
41
|
+
|
|
42
|
+
if (packageDeprecated) {
|
|
43
|
+
throw new Error(`Package deprecated: ${packageDeprecated}`)
|
|
44
|
+
}
|
|
40
45
|
}
|
|
41
46
|
}
|
|
42
47
|
|
package/lib/marshalls/index.js
CHANGED
|
@@ -25,9 +25,10 @@ class NewBinMarshall extends BaseMarshall {
|
|
|
25
25
|
return
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const targetVersionString = await this.
|
|
28
|
+
const targetVersionString = await this.resolvePackageVersion(
|
|
29
29
|
pkg.packageName,
|
|
30
|
-
pkg.packageVersion
|
|
30
|
+
pkg.packageVersion,
|
|
31
|
+
fullPackageInfo
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
if (!targetVersionString || !fullPackageInfo.versions[targetVersionString]) {
|
|
@@ -55,7 +56,9 @@ class NewBinMarshall extends BaseMarshall {
|
|
|
55
56
|
previousVersionData.name || pkg.packageName
|
|
56
57
|
)
|
|
57
58
|
|
|
58
|
-
const newBinaries = Object.keys(newBin).filter(
|
|
59
|
+
const newBinaries = Object.keys(newBin).filter(
|
|
60
|
+
(key) => !Object.prototype.hasOwnProperty.call(oldBin, key)
|
|
61
|
+
)
|
|
59
62
|
|
|
60
63
|
if (newBinaries.length > 0) {
|
|
61
64
|
newBinaries.forEach((binaryName) => {
|
|
@@ -22,6 +22,8 @@ class Marshall extends BaseMarshall {
|
|
|
22
22
|
validate(pkg) {
|
|
23
23
|
const validationMetadata = {}
|
|
24
24
|
|
|
25
|
+
// removed debug log
|
|
26
|
+
|
|
25
27
|
return (
|
|
26
28
|
this.packageRepoUtils
|
|
27
29
|
.getPackageInfo(pkg.packageName)
|
|
@@ -188,7 +190,6 @@ class Marshall extends BaseMarshall {
|
|
|
188
190
|
const registryKeysEndpoint = '/-/npm/v1/keys'
|
|
189
191
|
|
|
190
192
|
const registryKeysUrl = `${registryHost}${registryKeysEndpoint}`
|
|
191
|
-
// eslint-disable-next-line no-undef
|
|
192
193
|
return fetch(registryKeysUrl)
|
|
193
194
|
.then((response) => {
|
|
194
195
|
return response.json()
|
|
@@ -30,7 +30,7 @@ class Marshall extends BaseMarshall {
|
|
|
30
30
|
try {
|
|
31
31
|
urlStructure = new URL(lastVersionData.repository.url)
|
|
32
32
|
urlOfGitRepository = new URL(`https://${urlStructure.host}${urlStructure.pathname}`)
|
|
33
|
-
} catch
|
|
33
|
+
} catch {
|
|
34
34
|
throw new Warning('No valid repository is associated with the package')
|
|
35
35
|
}
|
|
36
36
|
return fetch(urlOfGitRepository.href).catch(() => {
|
|
@@ -16,40 +16,46 @@ class Marshall extends BaseMarshall {
|
|
|
16
16
|
return 'Checking package for pre/post install scripts'
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
validate(pkg) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (!packageVersion) {
|
|
27
|
-
return true
|
|
28
|
-
}
|
|
19
|
+
async validate(pkg) {
|
|
20
|
+
const data = await this.packageRepoUtils.getPackageInfo(pkg.packageName)
|
|
21
|
+
|
|
22
|
+
if (!data) {
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// blacklisted scripts due to possible malicious intent:
|
|
37
|
-
const blacklistScripts = ['install', 'preinstall', 'postinstall']
|
|
38
|
-
|
|
39
|
-
blacklistScripts.forEach((scriptName) => {
|
|
40
|
-
if (
|
|
41
|
-
packageScripts &&
|
|
42
|
-
packageScripts.hasOwnProperty(scriptName) &&
|
|
43
|
-
packageScripts[scriptName].length > 0
|
|
44
|
-
) {
|
|
45
|
-
throw new Error(
|
|
46
|
-
`Detected a possible malicious intent script, audit required: ${scriptName}: ${packageScripts[scriptName]}`
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
})
|
|
26
|
+
const packageVersion = await this.resolvePackageVersion(
|
|
27
|
+
pkg.packageName,
|
|
28
|
+
pkg.packageVersion,
|
|
29
|
+
data
|
|
30
|
+
)
|
|
50
31
|
|
|
32
|
+
if (!packageVersion) {
|
|
51
33
|
return true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const packageScripts =
|
|
37
|
+
data &&
|
|
38
|
+
data.versions &&
|
|
39
|
+
data.versions[packageVersion] &&
|
|
40
|
+
data.versions[packageVersion].scripts
|
|
41
|
+
|
|
42
|
+
// blacklisted scripts due to possible malicious intent:
|
|
43
|
+
const blacklistScripts = ['install', 'preinstall', 'postinstall']
|
|
44
|
+
|
|
45
|
+
blacklistScripts.forEach((scriptName) => {
|
|
46
|
+
if (
|
|
47
|
+
packageScripts &&
|
|
48
|
+
packageScripts[scriptName] &&
|
|
49
|
+
String(packageScripts[scriptName]).length > 0
|
|
50
|
+
) {
|
|
51
|
+
const scriptContent = packageScripts[scriptName]
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Detected a possible malicious intent script, audit required: ${scriptName}: ${scriptContent}`
|
|
54
|
+
)
|
|
55
|
+
}
|
|
52
56
|
})
|
|
57
|
+
|
|
58
|
+
return true
|
|
53
59
|
}
|
|
54
60
|
}
|
|
55
61
|
|
|
@@ -63,7 +63,6 @@ class Marshall extends BaseMarshall {
|
|
|
63
63
|
const registryKeysEndpoint = '/-/npm/v1/keys'
|
|
64
64
|
|
|
65
65
|
const registryKeysUrl = `${registryHost}${registryKeysEndpoint}`
|
|
66
|
-
// eslint-disable-next-line no-undef
|
|
67
66
|
return fetch(registryKeysUrl)
|
|
68
67
|
.then((response) => {
|
|
69
68
|
return response.json()
|
|
@@ -23,7 +23,6 @@ class Marshall extends BaseMarshall {
|
|
|
23
23
|
validate(pkg) {
|
|
24
24
|
let levenshteinDistance = null
|
|
25
25
|
let similarPackages = []
|
|
26
|
-
let packageFoundInTopPackages = false
|
|
27
26
|
return new Promise((resolve, reject) => {
|
|
28
27
|
// If package is within an allow-list
|
|
29
28
|
if (this.packageRepoUtils.isPackageInAllowList(pkg.packageName)) {
|
|
@@ -34,7 +33,6 @@ class Marshall extends BaseMarshall {
|
|
|
34
33
|
// If the package to be installed is itself found within the Top Packages dataset
|
|
35
34
|
// then we don't report on it
|
|
36
35
|
if (pkg.packageName === popularPackageNameInRepository) {
|
|
37
|
-
packageFoundInTopPackages = true
|
|
38
36
|
return resolve([])
|
|
39
37
|
}
|
|
40
38
|
|
|
@@ -17,50 +17,45 @@ class Marshall extends BaseMarshall {
|
|
|
17
17
|
return 'Checking version maturity'
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
validate(pkg) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return pkg
|
|
63
|
-
})
|
|
20
|
+
async validate(pkg) {
|
|
21
|
+
const data = await this.packageRepoUtils.getPackageInfo(pkg.packageName)
|
|
22
|
+
|
|
23
|
+
if (!data || !data.time) {
|
|
24
|
+
throw new Error('Could not determine package version information')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const packageVersion = await this.resolvePackageVersion(
|
|
28
|
+
pkg.packageName,
|
|
29
|
+
pkg.packageVersion,
|
|
30
|
+
data
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if (!packageVersion || !Object.prototype.hasOwnProperty.call(data.time, packageVersion)) {
|
|
34
|
+
throw new Error(`Could not determine release date for version ${packageVersion}`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const versionReleaseDate = data.time[packageVersion]
|
|
38
|
+
const versionDateDiff = new Date() - new Date(versionReleaseDate)
|
|
39
|
+
const versionDateDiffInDays = Math.round(versionDateDiff / (1000 * 60 * 60 * 24))
|
|
40
|
+
|
|
41
|
+
if (versionDateDiffInDays < VERSION_AGE_THRESHOLD) {
|
|
42
|
+
let timeAgoText = 'days'
|
|
43
|
+
let timeAgoNumber = versionDateDiffInDays
|
|
44
|
+
|
|
45
|
+
if (versionDateDiffInDays === 0) {
|
|
46
|
+
timeAgoText = 'hours'
|
|
47
|
+
const versionDateDiffInHours = Math.round(versionDateDiff / (1000 * 60 * 60))
|
|
48
|
+
timeAgoNumber = versionDateDiffInHours
|
|
49
|
+
} else if (versionDateDiffInDays === 1) {
|
|
50
|
+
timeAgoText = 'day'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Detected a recently published version (published ${timeAgoNumber} ${timeAgoText} ago) - consider waiting for community review`
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return pkg
|
|
64
59
|
}
|
|
65
60
|
}
|
|
66
61
|
|
package/lib/packageManager.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "npq",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.4",
|
|
4
4
|
"description": "marshall your npm/npm package installs with high quality and class 🎖",
|
|
5
5
|
"bin": {
|
|
6
6
|
"npq": "./bin/npq.js",
|
|
@@ -9,19 +9,16 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"lint": "eslint && npm run lint:lockfile",
|
|
11
11
|
"lint:lockfile": "lockfile-lint --path package-lock.json --type npm --validate-https --allowed-hosts npm npm",
|
|
12
|
-
"
|
|
12
|
+
"fix": "eslint --fix",
|
|
13
|
+
"test": "jest",
|
|
13
14
|
"build": "node scripts/build.js",
|
|
14
|
-
"test:watch": "
|
|
15
|
-
"coverage:view": "opn coverage/lcov-report/index.html",
|
|
15
|
+
"test:watch": "jest --watch",
|
|
16
16
|
"commit": "git-cz",
|
|
17
17
|
"format": "prettier --config .prettierrc.js --write \"**/*.js\"",
|
|
18
|
-
"docs": "npm run docs:code && npm run docs:api",
|
|
19
|
-
"docs:api": "doxdox \"*.js\" --layout bootstrap --output docs/index.html",
|
|
20
|
-
"docs:code": "docco \"*.js\" --output docs/code",
|
|
21
18
|
"semantic-release": "npx semantic-release",
|
|
22
19
|
"#postinstall": "node scripts/postinstall.js",
|
|
23
20
|
"#preuninstall": "node scripts/preuninstall.js",
|
|
24
|
-
"
|
|
21
|
+
"prepare": "husky || true"
|
|
25
22
|
},
|
|
26
23
|
"engines": {
|
|
27
24
|
"node": ">=20.13.0"
|
|
@@ -52,59 +49,21 @@
|
|
|
52
49
|
"semver": "^7.7.2"
|
|
53
50
|
},
|
|
54
51
|
"devDependencies": {
|
|
55
|
-
"@babel/core": "^7.28.0",
|
|
56
|
-
"@babel/plugin-transform-runtime": "^7.28.0",
|
|
57
|
-
"@babel/preset-env": "^7.28.0",
|
|
58
|
-
"@babel/runtime": "^7.28.2",
|
|
59
|
-
"@babel/types": "^7.28.2",
|
|
60
52
|
"@commitlint/cli": "^19.8.1",
|
|
61
53
|
"@commitlint/config-angular": "^19.8.1",
|
|
54
|
+
"@eslint/js": "^9.32.0",
|
|
62
55
|
"commitizen": "^4.3.1",
|
|
63
|
-
"cross-env": "^10.0.0",
|
|
64
56
|
"cz-conventional-changelog": "^3.3.0",
|
|
65
57
|
"eslint": "^9.32.0",
|
|
66
|
-
"eslint-plugin-
|
|
58
|
+
"eslint-plugin-n": "^17.21.3",
|
|
67
59
|
"eslint-plugin-security": "^3.0.1",
|
|
60
|
+
"globals": "^16.3.0",
|
|
68
61
|
"husky": "^9.1.7",
|
|
69
62
|
"jest": "^30.0.5",
|
|
70
63
|
"lint-staged": "^16.1.4",
|
|
71
64
|
"lockfile-lint": "^4.14.1",
|
|
72
65
|
"prettier": "^3.6.2"
|
|
73
66
|
},
|
|
74
|
-
"jest": {
|
|
75
|
-
"testEnvironment": "node",
|
|
76
|
-
"verbose": true,
|
|
77
|
-
"notify": false,
|
|
78
|
-
"collectCoverage": true,
|
|
79
|
-
"coverageThreshold": {
|
|
80
|
-
"global": {
|
|
81
|
-
"branches": 75,
|
|
82
|
-
"functions": 75,
|
|
83
|
-
"lines": 75,
|
|
84
|
-
"statements": 75
|
|
85
|
-
},
|
|
86
|
-
"scripts/*": {
|
|
87
|
-
"branches": 60,
|
|
88
|
-
"functions": 90,
|
|
89
|
-
"lines": 80,
|
|
90
|
-
"statements": 80
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
"testMatch": [
|
|
94
|
-
"**/__tests__/**/*.test.js"
|
|
95
|
-
],
|
|
96
|
-
"testPathIgnorePatterns": [
|
|
97
|
-
"/__tests__/__fixtures__/*"
|
|
98
|
-
]
|
|
99
|
-
},
|
|
100
|
-
"babel": {
|
|
101
|
-
"presets": [
|
|
102
|
-
"@babel/env"
|
|
103
|
-
],
|
|
104
|
-
"plugins": [
|
|
105
|
-
"@babel/plugin-transform-runtime"
|
|
106
|
-
]
|
|
107
|
-
},
|
|
108
67
|
"config": {
|
|
109
68
|
"commitizen": {
|
|
110
69
|
"path": "./node_modules/cz-conventional-changelog"
|
|
@@ -118,72 +77,9 @@
|
|
|
118
77
|
"lint-staged": {
|
|
119
78
|
"**/*.js": [
|
|
120
79
|
"npm run format",
|
|
121
|
-
"npm run lint"
|
|
122
|
-
"git add"
|
|
80
|
+
"npm run lint"
|
|
123
81
|
]
|
|
124
82
|
},
|
|
125
|
-
"eslintConfig": {
|
|
126
|
-
"env": {
|
|
127
|
-
"node": true,
|
|
128
|
-
"es6": true,
|
|
129
|
-
"jest": true
|
|
130
|
-
},
|
|
131
|
-
"plugins": [
|
|
132
|
-
"node",
|
|
133
|
-
"security"
|
|
134
|
-
],
|
|
135
|
-
"extends": [
|
|
136
|
-
"plugin:node/recommended",
|
|
137
|
-
"plugin:security/recommended"
|
|
138
|
-
],
|
|
139
|
-
"rules": {
|
|
140
|
-
"semi": "off",
|
|
141
|
-
"no-process-exit": "off",
|
|
142
|
-
"node/no-unsupported-features": "off",
|
|
143
|
-
"node/no-unpublished-require": "off",
|
|
144
|
-
"security/detect-non-literal-fs-filename": "warn",
|
|
145
|
-
"security/detect-unsafe-regex": "warn",
|
|
146
|
-
"security/detect-buffer-noassert": "error",
|
|
147
|
-
"security/detect-child-process": "warn",
|
|
148
|
-
"security/detect-disable-mustache-escape": "error",
|
|
149
|
-
"security/detect-eval-with-expression": "error",
|
|
150
|
-
"security/detect-no-csrf-before-method-override": "error",
|
|
151
|
-
"security/detect-non-literal-regexp": "error",
|
|
152
|
-
"security/detect-non-literal-require": "warn",
|
|
153
|
-
"security/detect-object-injection": "off",
|
|
154
|
-
"security/detect-possible-timing-attacks": "error",
|
|
155
|
-
"security/detect-pseudoRandomBytes": "error",
|
|
156
|
-
"node/no-unsupported-features/node-builtins": [
|
|
157
|
-
"error",
|
|
158
|
-
{
|
|
159
|
-
"version": ">=18.17.0",
|
|
160
|
-
"ignores": []
|
|
161
|
-
}
|
|
162
|
-
],
|
|
163
|
-
"node/no-unsupported-features/es-syntax": [
|
|
164
|
-
"error",
|
|
165
|
-
{
|
|
166
|
-
"version": ">=18.17.0",
|
|
167
|
-
"ignores": []
|
|
168
|
-
}
|
|
169
|
-
]
|
|
170
|
-
},
|
|
171
|
-
"overrides": [
|
|
172
|
-
{
|
|
173
|
-
"files": [
|
|
174
|
-
"__tests__/*"
|
|
175
|
-
],
|
|
176
|
-
"rules": {
|
|
177
|
-
"node/no-unsupported-features/es-syntax": "off"
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
],
|
|
181
|
-
"parserOptions": {
|
|
182
|
-
"ecmaFeatures": {
|
|
183
|
-
"impliedStrict": true
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
83
|
"release": {
|
|
188
84
|
"branches": [
|
|
189
85
|
"main"
|
package/scripts/build.js
CHANGED
package/scripts/postinstall.js
CHANGED
package/scripts/preuninstall.js
CHANGED