@nodesecure/tarball
Version:
NodeSecure tarball scanner
139 lines • 6.21 kB
JavaScript
// Import Node.js Dependencies
import path from "node:path";
import os from "node:os";
// Import Third-party Dependencies
import {} from "@nodesecure/js-x-ray";
import * as conformance from "@nodesecure/conformance";
import { ManifestManager } from "@nodesecure/mama";
import pacote from "pacote";
// Import Internal Dependencies
import { isSensitiveFile, booleanToFlags } from "./utils/index.js";
import { NpmTarball } from "./class/NpmTarball.class.js";
import { DependencyCollectableSet } from "./class/DependencyCollectableSet.class.js";
import { getEmptyPackageWarning, getSemVerWarning } from "./warnings.js";
import { NATIVE_CODE_EXTENSIONS, NPM_TOKEN } from "./constants.js";
export async function scanPackageCore(locationOrManifest, astAnalyserOptions) {
const mama = await ManifestManager.fromPackageJSON(locationOrManifest);
const dependencySet = new DependencyCollectableSet(mama);
const tarex = new NpmTarball(mama);
const { composition, conformance: conformanceResult, code } = await tarex.scanFiles({
...astAnalyserOptions,
collectables: [
...astAnalyserOptions?.collectables ?? [],
dependencySet
]
});
const warnings = [];
if (composition.files.length === 1 && composition.files.includes("package.json")) {
warnings.push(getEmptyPackageWarning());
}
if (mama.hasZeroSemver) {
warnings.push(getSemVerWarning(mama.document.version));
}
warnings.push(...code.warnings);
const { files, dependencies, flags } = dependencySet.extract();
const { description, engines, repository, scripts } = mama.document;
return {
description,
engines,
repository,
scripts,
author: mama.author,
integrity: mama.isWorkspace ? null : mama.integrity,
type: mama.moduleType,
size: composition.size,
licenses: conformanceResult.licenses,
uniqueLicenseIds: conformanceResult.uniqueLicenseIds,
warnings,
flags: Array.from(booleanToFlags({
...flags,
hasExternalCapacity: code.flags.hasExternalCapacity || flags.hasExternalCapacity,
hasNoLicense: conformanceResult.uniqueLicenseIds.length === 0,
hasMultipleLicenses: conformanceResult.uniqueLicenseIds.length > 1,
hasMinifiedCode: code.minified.length > 0,
hasWarnings: warnings.length > 0,
hasBannedFile: composition.files.some((filePath) => isSensitiveFile(filePath)),
hasNativeCode: mama.flags.isNative ||
composition.files.some((file) => NATIVE_CODE_EXTENSIONS.has(path.extname(file))),
hasScript: mama.flags.hasUnsafeScripts
})),
composition: {
extensions: [...composition.ext],
files: composition.files,
minified: code.minified,
unused: dependencies.unused,
missing: dependencies.missing,
required_files: [...files],
required_nodejs: dependencies.nodeJs,
required_thirdparty: dependencies.thirdparty,
required_subpath: dependencies.subpathImports
},
path: code.path
};
}
export async function scanDirOrArchive(locationOrManifest, ref, options = {}) {
const result = await scanPackageCore(locationOrManifest, options.astAnalyserOptions);
const { description, engines, repository, scripts, author, integrity } = result;
Object.assign(ref, { description, engines, repository, scripts, author, integrity });
ref.warnings.push(...result.warnings);
ref.licenses = result.licenses;
ref.uniqueLicenseIds = result.uniqueLicenseIds;
ref.type = result.type;
ref.size = result.size;
ref.composition.extensions.push(...result.composition.extensions);
ref.composition.files.push(...result.composition.files);
ref.composition.minified = result.composition.minified;
ref.composition.unused.push(...result.composition.unused);
ref.composition.missing.push(...result.composition.missing);
ref.composition.required_files = result.composition.required_files;
ref.composition.required_nodejs = result.composition.required_nodejs;
ref.composition.required_thirdparty = result.composition.required_thirdparty;
ref.composition.required_subpath = result.composition.required_subpath;
ref.path = result.path;
const flags = result.flags.filter((flag) => flag !== "hasWarnings" || !ref.flags.includes("hasWarnings"));
ref.flags.push(...flags);
}
export async function scanPackage(manifestOrLocation, options = {}) {
const { astAnalyserOptions } = options;
const mama = await ManifestManager.fromPackageJSON(manifestOrLocation);
const extractor = new NpmTarball(mama);
const dependencySet = new DependencyCollectableSet(mama);
const { composition, conformance: conformanceResult, code } = await extractor.scanFiles({
...astAnalyserOptions,
collectables: [
...(astAnalyserOptions?.collectables ?? []),
dependencySet
]
});
const warnings = [...code.warnings];
if (composition.files.length === 1 && composition.files.includes("package.json")) {
warnings.push(getEmptyPackageWarning());
}
return {
files: {
list: composition.files,
extensions: [...composition.ext],
minified: code.minified
},
directorySize: composition.size,
uniqueLicenseIds: conformanceResult.uniqueLicenseIds,
licenses: conformanceResult.licenses,
ast: {
dependencies: dependencySet.dependencies,
warnings
},
path: code.path
};
}
export async function extractAndResolve(location, options) {
const { spec, registry, pacoteProvider = pacote } = options;
const tarballLocation = path.join(location, spec.replaceAll("/", "_"));
await pacoteProvider.extract(spec, tarballLocation, {
...NPM_TOKEN,
registry,
cache: `${os.homedir()}/.npm`,
userAgent: `@nodesecure/tarball node/${process.version}`
});
return ManifestManager.fromPackageJSON(tarballLocation);
}
//# sourceMappingURL=tarball.js.map