npq 3.11.3 → 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/lib/marshalls/age.marshall.js +35 -36
- package/lib/marshalls/baseMarshall.js +26 -0
- package/lib/marshalls/deprecation.marshall.js +26 -21
- package/lib/marshalls/newbin.marshall.js +3 -2
- package/lib/marshalls/provenance.marshall.js +2 -0
- package/lib/marshalls/scripts.marshall.js +36 -30
- package/lib/marshalls/version-maturity.marshall.js +39 -44
- package/package.json +1 -1
|
@@ -19,48 +19,47 @@ class Marshall extends BaseMarshall {
|
|
|
19
19
|
return 'Checking package maturity'
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
validate(pkg) {
|
|
23
|
-
|
|
24
|
-
return this.packageRepoUtils
|
|
25
|
-
.getPackageInfo(pkg.packageName)
|
|
26
|
-
.then((data) => {
|
|
27
|
-
if (data && data.time && data.time.created) {
|
|
28
|
-
packageData = data
|
|
29
|
-
const pkgCreatedDate = data.time.created
|
|
30
|
-
const dateDiff = Date.now() - Date.parse(pkgCreatedDate)
|
|
22
|
+
async validate(pkg) {
|
|
23
|
+
const data = await this.packageRepoUtils.getPackageInfo(pkg.packageName)
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
36
|
-
}
|
|
25
|
+
if (!data || !data.time || !data.time.created) {
|
|
26
|
+
throw new Warning('Could not determine package age')
|
|
27
|
+
}
|
|
37
28
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
throw new Warning('Could not determine package age')
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
.then((pkg) => {
|
|
44
|
-
return this.packageRepoUtils.getSemVer(pkg.packageName, pkg.packageVersion)
|
|
45
|
-
})
|
|
46
|
-
.then((versionResolved) => {
|
|
47
|
-
const versionReleaseDate = packageData.time[versionResolved]
|
|
48
|
-
const versionDateDiff = new Date() - new Date(versionReleaseDate)
|
|
29
|
+
const pkgCreatedDate = data.time.created
|
|
30
|
+
const dateDiff = Date.now() - Date.parse(pkgCreatedDate)
|
|
49
31
|
|
|
50
|
-
|
|
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
|
+
}
|
|
51
37
|
|
|
52
|
-
|
|
53
|
-
|
|
38
|
+
const packageVersion = await this.resolvePackageVersion(
|
|
39
|
+
pkg.packageName,
|
|
40
|
+
pkg.packageVersion,
|
|
41
|
+
data
|
|
42
|
+
)
|
|
54
43
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
44
|
+
if (!packageVersion || !data.time[packageVersion]) {
|
|
45
|
+
throw new Warning('Could not determine package version release date')
|
|
46
|
+
}
|
|
59
47
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
+
}
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
|
|
@@ -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
|
|
|
@@ -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]) {
|
|
@@ -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
|
-
Object.prototype.hasOwnProperty.call(packageScripts, 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
|
|
|
@@ -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
|
|