@nodesecure/scanner
Version:
A package API to run a static analysis of your module's dependencies.
127 lines • 5.1 kB
JavaScript
// Import Third-party Dependencies
import semver from "semver";
import * as npmRegistrySDK from "@nodesecure/npm-registry-sdk";
import { packageJSONIntegrityHash } from "@nodesecure/mama";
// Import Internal Dependencies
import { getLinks } from "./utils/index.js";
import { Logger } from "./class/logger.class.js";
export async function manifestMetadata(name, version, dependency) {
try {
const pkgVersion = await npmRegistrySDK.packumentVersion(name, version);
const integrity = packageJSONIntegrityHash(pkgVersion, {
isFromRemoteRegistry: true
});
Object.assign(dependency.versions[version], {
links: getLinks(pkgVersion)
});
dependency.metadata.integrity[version] = integrity;
}
catch {
// Ignore
}
}
export async function packageMetadata(name, version, options) {
const { dependency, logger } = options;
const spec = `${name}:${version}`;
try {
const pkg = await npmRegistrySDK.packument(name);
const oneYearFromToday = new Date();
oneYearFromToday.setFullYear(oneYearFromToday.getFullYear() - 1);
const lastVersion = pkg["dist-tags"].latest;
const lastUpdateAt = new Date(pkg.time[lastVersion]);
const metadata = {
author: pkg.author ?? null,
homepage: pkg.homepage || null,
publishedCount: Object.values(pkg.versions).length,
lastVersion,
lastUpdateAt,
hasReceivedUpdateInOneYear: !(oneYearFromToday > lastUpdateAt),
hasManyPublishers: false,
hasChangedAuthor: false,
maintainers: pkg.maintainers ?? [],
publishers: [],
integrity: {}
};
const isOutdated = semver.neq(version, lastVersion);
const dependencyVersion = dependency.versions[version];
const flags = dependencyVersion.flags;
if (isOutdated) {
flags.push("isOutdated");
}
const publishers = new Set();
let searchForMaintainersInVersions = metadata.maintainers.length === 0;
for (const ver of Object.values(pkg.versions).reverse()) {
if (spec === `${ver.name}:${ver.version}`) {
if ("deprecated" in ver && !flags.includes("isDeprecated")) {
flags.push("isDeprecated");
dependencyVersion.deprecated = ver.deprecated;
}
metadata.integrity[ver.version] = packageJSONIntegrityHash(ver, { isFromRemoteRegistry: true });
}
const { _npmUser = null, version, maintainers = [] } = ver;
if (_npmUser !== null) {
const authorName = metadata.author?.name ?? null;
if (authorName === null) {
metadata.author = _npmUser;
}
else if (authorName !== null && _npmUser.name !== authorName) {
metadata.hasManyPublishers = true;
}
if (!publishers.has(_npmUser.name)) {
publishers.add(_npmUser.name);
metadata.publishers.push({
..._npmUser,
version,
at: new Date(pkg.time[version]).toISOString()
});
}
}
if (searchForMaintainersInVersions) {
metadata.maintainers.push(...maintainers);
searchForMaintainersInVersions = false;
}
}
await addNpmAvatar(metadata);
Object.assign(dependency.versions[version], { links: getLinks(pkg.versions[version]) });
dependency.metadata = metadata;
}
catch {
// ignore
}
finally {
logger.tick("registry");
}
}
async function addNpmAvatar(metadata) {
const contributors = [
...metadata.maintainers,
...metadata.publishers
];
if (metadata.author !== null) {
contributors.push(metadata.author);
}
const emailToAvatar = {};
const promises = contributors.map((contributor) => {
if (contributor.email && emailToAvatar[contributor.email]) {
contributor.npmAvatar = emailToAvatar[contributor.email];
return Promise.resolve();
}
return npmRegistrySDK.user(contributor.name, { perPage: 1 })
.then((profile) => {
contributor.npmAvatar = profile.avatars.small;
if (contributor.email && contributor.npmAvatar) {
emailToAvatar[contributor.email] = contributor.npmAvatar;
}
}).catch(() => {
contributor.npmAvatar = undefined;
});
});
await Promise.all(promises);
// back fill npmAvatar if any name property was not npm username in first pass
for (const contributor of contributors) {
if (!contributor.npmAvatar && contributor.email && emailToAvatar[contributor.email]) {
contributor.npmAvatar = emailToAvatar[contributor.email];
}
}
}
//# sourceMappingURL=npmRegistry.js.map