mcp-ai-agent-guidelines
Version:
A comprehensive Model Context Protocol server providing advanced tools, resources, and prompts for implementing AI agent best practices
196 lines • 8.15 kB
JavaScript
/**
* Yarn Lock Parser (yarn.lock)
*
* Parses yarn.lock files in both v1 and v2 formats.
* V1 uses a custom format, V2 uses YAML-like syntax.
*/
import { BaseParser } from "./base.js";
export class YarnLockParser extends BaseParser {
static deprecatedPackages = {
request: {
reason: "Deprecated since 2020",
alternative: "Use axios, node-fetch, or native fetch",
},
"node-uuid": {
reason: "Renamed to uuid",
alternative: "Use uuid package",
},
colors: {
reason: "Security concerns and maintenance issues",
alternative: "Use chalk or picocolors",
},
faker: {
reason: "Original package deprecated",
alternative: "Use @faker-js/faker",
},
tslint: {
reason: "Deprecated in favor of ESLint",
alternative: "Use @typescript-eslint/eslint-plugin",
},
};
parse(content) {
const packages = [];
const errors = [];
const seenPackages = new Set();
try {
// Detect version
const isV1 = content.includes("# yarn lockfile v1");
const isV2 = content.includes("__metadata:") || content.includes("resolution:");
if (isV1) {
this.parseV1(content, packages, seenPackages);
}
else if (isV2) {
this.parseV2(content, packages, seenPackages);
}
else {
// Try V1 format as default
this.parseV1(content, packages, seenPackages);
}
}
catch (error) {
errors.push(`Error parsing yarn.lock: ${error instanceof Error ? error.message : "Unknown error"}`);
}
return {
packages,
ecosystem: "javascript",
fileType: "yarn.lock",
errors: errors.length > 0 ? errors : undefined,
};
}
parseV1(content, packages, seenPackages) {
// V1 format: package@version: followed by indented properties
const blocks = content.split(/\n(?=[^\s#])/);
for (const block of blocks) {
if (!block.trim() || block.startsWith("#"))
continue;
// Simplified pattern to avoid ReDoS - match first package name only
const headerMatch = block.match(/^["']?([^@\s]+)@[^:\s]+/);
if (!headerMatch)
continue;
// Verify it ends with a colon (package definition)
const firstLine = block.split("\n")[0];
if (!firstLine.endsWith(":"))
continue;
const packageName = headerMatch[1];
const versionMatch = block.match(/^\s*version\s+["']([^"']+)["']/m);
const version = versionMatch ? versionMatch[1] : "*";
const packageKey = `${packageName}@${version}`;
if (seenPackages.has(packageKey))
continue;
seenPackages.add(packageKey);
packages.push({
name: packageName,
version,
type: "dependencies",
ecosystem: "javascript",
});
}
}
parseV2(content, packages, seenPackages) {
// V2 format: "package@npm:^version" entries with resolution field
const lines = content.split("\n");
let currentPackage = null;
let currentVersion = null;
for (const line of lines) {
// Skip metadata and comments
if (line.startsWith("__metadata:") || line.startsWith("#"))
continue;
// Simplified pattern to avoid ReDoS - match first package name only
// Must end with : and contain @npm: or just @
if (line.endsWith(":") && line.includes("@")) {
const headerMatch = line.match(/^["']?([^@\s]+)@/);
if (headerMatch) {
currentPackage = headerMatch[1];
currentVersion = null;
continue;
}
}
// Match version field
if (currentPackage) {
const versionMatch = line.match(/^\s*version:\s*["']?([^"'\s]+)["']?/);
if (versionMatch) {
currentVersion = versionMatch[1];
const packageKey = `${currentPackage}@${currentVersion}`;
if (!seenPackages.has(packageKey)) {
seenPackages.add(packageKey);
packages.push({
name: currentPackage,
version: currentVersion,
type: "dependencies",
ecosystem: "javascript",
});
}
currentPackage = null;
currentVersion = null;
}
}
}
}
canParse(content) {
// Check for yarn.lock v1 header
if (content.includes("# yarn lockfile v1"))
return true;
// Check for yarn.lock v2 markers
if (content.includes("__metadata:"))
return true;
// Check for common yarn.lock patterns
const hasVersionField = /^\s*version\s+["'][^"']+["']/m.test(content);
const hasResolvedField = /^\s*resolved\s+["'][^"']+["']/m.test(content);
const hasPackagePattern = /^["']?[^@\s]+@[^:]+["']?:/m.test(content);
return hasPackagePattern && (hasVersionField || hasResolvedField);
}
getEcosystem() {
return "javascript";
}
getFileTypes() {
return ["yarn.lock"];
}
analyzePackage(pkg, options, issues) {
const pattern = this.checkVersionPattern(pkg.version);
if (options.checkOutdated) {
if (pattern.isPreRelease) {
this.addIssue(issues, pkg, "Pre-1.0 Version", "info", "Package is pre-1.0, which may indicate instability", "Check if a stable 1.x+ version is available");
}
}
if (options.checkDeprecated) {
const deprecated = YarnLockParser.deprecatedPackages[pkg.name];
if (deprecated) {
this.addIssue(issues, pkg, "Deprecated Package", "high", deprecated.reason, deprecated.alternative);
}
}
if (options.checkVulnerabilities) {
this.checkKnownVulnerabilities(pkg, issues);
}
}
checkVersionPattern(version) {
return {
isPreRelease: /^0\.[0-9]+\.[0-9]+/.test(version),
};
}
checkKnownVulnerabilities(pkg, issues) {
// Lodash vulnerabilities
if (pkg.name === "lodash") {
if (pkg.version.match(/^[0-3]\./) ||
pkg.version.match(/^4\.(0|1[0-6]|17\.(0|1[0-9]|20))($|[^\d])/)) {
this.addIssue(issues, pkg, "Known Vulnerabilities", "moderate", "Lodash versions below 4.17.21 have known security vulnerabilities", "Update to lodash@4.17.21 or use lodash-es");
}
}
// Moment.js deprecation
if (pkg.name === "moment") {
this.addIssue(issues, pkg, "Deprecated & Bundle Size", "moderate", "Moment.js is in maintenance mode and has large bundle size", "Consider migrating to date-fns, dayjs, or Temporal API");
}
// Axios vulnerabilities
if (pkg.name === "axios") {
if (pkg.version.match(/^0\./) || pkg.version.match(/^1\.[0-5]($|\.)/)) {
this.addIssue(issues, pkg, "Known Vulnerabilities", "high", "Axios versions below 1.6.0 have known security vulnerabilities", "Update to axios@1.6.0 or later");
}
}
}
addEcosystemRecommendations(recommendations) {
recommendations.push("Run 'yarn audit' for detailed vulnerability analysis");
recommendations.push("Run 'yarn outdated' to check for latest versions");
recommendations.push("Run 'yarn upgrade-interactive' for guided updates");
recommendations.push("Consider using 'yarn dedupe' to reduce duplicate packages");
}
}
//# sourceMappingURL=yarn.js.map