agentshield/analysis/
supply_chain.rs1use crate::ir::dependency_surface::{DependencyIssue, DependencyIssueType, DependencySurface};
4
5const POPULAR_PYTHON_PACKAGES: &[&str] = &[
7 "requests",
8 "flask",
9 "django",
10 "numpy",
11 "pandas",
12 "scipy",
13 "boto3",
14 "fastapi",
15 "uvicorn",
16 "httpx",
17 "aiohttp",
18 "pillow",
19 "pydantic",
20 "sqlalchemy",
21 "celery",
22 "redis",
23 "psycopg2",
24 "pytest",
25 "setuptools",
26 "cryptography",
27 "paramiko",
28 "pyyaml",
29 "jinja2",
30 "beautifulsoup4",
31 "selenium",
32 "scrapy",
33 "tensorflow",
34 "pytorch",
35 "transformers",
36 "langchain",
37 "openai",
38 "anthropic",
39];
40
41const POPULAR_NPM_PACKAGES: &[&str] = &[
43 "express",
44 "react",
45 "lodash",
46 "axios",
47 "chalk",
48 "commander",
49 "next",
50 "typescript",
51 "webpack",
52 "eslint",
53 "prettier",
54 "jest",
55 "mongoose",
56 "sequelize",
57 "prisma",
58 "fastify",
59 "socket.io",
60 "dotenv",
61 "cors",
62 "jsonwebtoken",
63 "bcrypt",
64 "nodemailer",
65 "openai",
66 "langchain",
67 "zod",
68 "drizzle-orm",
69];
70
71pub fn check_typosquats(deps: &DependencySurface) -> Vec<DependencyIssue> {
76 let mut issues = Vec::new();
77
78 let all_popular: Vec<&str> = POPULAR_PYTHON_PACKAGES
79 .iter()
80 .chain(POPULAR_NPM_PACKAGES.iter())
81 .copied()
82 .collect();
83
84 for dep in &deps.dependencies {
85 let name = dep.name.to_lowercase();
86 for &popular in &all_popular {
87 if name == popular {
88 continue;
89 }
90 let distance = levenshtein::levenshtein(&name, popular);
91 if distance > 0 && distance <= 2 {
92 issues.push(DependencyIssue {
93 issue_type: DependencyIssueType::PossibleTyposquat,
94 package_name: dep.name.clone(),
95 description: format!(
96 "Package '{}' is similar to popular package '{}' (edit distance {})",
97 dep.name, popular, distance
98 ),
99 });
100 }
101 }
102 }
103
104 issues
105}