2828public class PackageLockJsonTest
2929{
3030 private static final Set <String > ALLOWED_DEPENDENCY_HOSTS = Set .of ("registry.npmjs.org" , "labkey.jfrog.io" );
31- // Allow-list of '@isaacs/cliui' dependencies
32- private static final Set <String > ALLOWED_NONSTANDARD_VERSIONS = Set .of ("npm:string-width@^4.2.0" , "npm:strip-ansi@^6.0.1" , "npm:wrap-ansi@^7.0.0" );
31+ private static final File SERVER_MODULES_DIR = new File (TestFileUtils .getLabKeyRoot (), "server/modules" );
3332
3433 private final List <String > errors = new ArrayList <>();
3534 private final File moduleDir ;
@@ -44,11 +43,10 @@ public static Collection<Object[]> data()
4443 {
4544 List <File > allModules = new ArrayList <>();
4645
47- File modulesDir = new File (TestFileUtils .getLabKeyRoot (), "server/modules" );
48- File [] files = modulesDir .listFiles ();
46+ File [] files = SERVER_MODULES_DIR .listFiles ();
4947 if (files == null )
5048 {
51- throw new RuntimeException ("No files found in modules directory: " + modulesDir .getAbsolutePath ());
49+ throw new RuntimeException ("No files found in modules directory: " + SERVER_MODULES_DIR .getAbsolutePath ());
5250 }
5351 for (File file : files )
5452 {
@@ -96,17 +94,19 @@ public void testPackageLock() throws Exception
9694 }
9795 }
9896
99- Assert .assertTrue ("Bad sources: " + errors , errors .isEmpty ());
97+ Assert .assertTrue ("Untrusted package sources:\n " + String . join ( " \n " , errors ) , errors .isEmpty ());
10098 }
10199
102100 /// Verify that a package reference in a package-lock.json file only resolves to known hosts and has a valid version
103101 /// Also checks sub-dependencies
104102 private void verifyPackage (String packageName , JSONObject packageJson , File packageLockFile )
105103 {
104+ String relPath = SERVER_MODULES_DIR .toPath ().relativize (packageLockFile .toPath ()).toString ();
105+
106106 String resolved = packageJson .optString ("resolved" );
107107 if (resolved .isBlank ())
108108 {
109- TestLogger .debug ("Resolved field is blank for package " + packageName + " in " + packageLockFile . getAbsolutePath () );
109+ TestLogger .debug ("Resolved field is blank for package " + packageName + " in " + relPath );
110110 }
111111 else
112112 {
@@ -116,14 +116,14 @@ private void verifyPackage(String packageName, JSONObject packageJson, File pack
116116 String host = resolvedURL .getHost ();
117117 if (!ALLOWED_DEPENDENCY_HOSTS .contains (host ))
118118 {
119- String message = "Package " + packageName + " resolved to unrecognized host [" + host + "] in " + packageLockFile . getAbsolutePath () ;
119+ String message = "Package " + packageName + " resolved to unrecognized host [" + host + "] in " + relPath ;
120120 errors .add (message );
121121 TestLogger .error (message );
122122 }
123123 }
124124 catch (URISyntaxException e )
125125 {
126- String message = "Package " + packageName + " resolved to an invalid location [" + resolved + "] in " + packageLockFile . getAbsolutePath () ;
126+ String message = "Package " + packageName + " resolved to an invalid location [" + resolved + "] in " + relPath ;
127127 errors .add (message );
128128 TestLogger .error (message );
129129 }
@@ -132,7 +132,7 @@ private void verifyPackage(String packageName, JSONObject packageJson, File pack
132132 String version = packageJson .optString ("version" );
133133 if (version .isBlank () || !CharUtils .isAsciiNumeric (version .charAt (0 )))
134134 {
135- String message = "Package " + packageName + " has bad version [" + version + "] in " + packageLockFile . getAbsolutePath () ;
135+ String message = "Package " + packageName + " has bad version [" + version + "] in " + relPath ;
136136 errors .add (message );
137137 TestLogger .error (message );
138138 }
@@ -148,9 +148,9 @@ private void verifyPackage(String packageName, JSONObject packageJson, File pack
148148 else
149149 {
150150 String tVer = transitiveDeps .optString (tDep );
151- if (tVer == null || tVer .contains ( ":" ) && ! ALLOWED_NONSTANDARD_VERSIONS .contains (tVer )) // URL, file, or workspace dependency
151+ if (tVer == null || tVer .replaceAll ( "^npm:" , "" ) .contains (":" )) // URL, file, or workspace dependency
152152 {
153- String message = "Package " + packageName + " has bad transitive dependency [" + tVer + "] in " + packageLockFile . getAbsolutePath () ;
153+ String message = "Package " + packageName + " has bad transitive dependency [" + tVer + "] in " + relPath ;
154154 errors .add (message );
155155 TestLogger .error (message );
156156 }
0 commit comments