Skip to content

Commit 3fa3c63

Browse files
committed
Refactor package-dependencies tool to support customizable library counting
- Modified the PackageDependencyExtractor class to allow dynamic specification of libraries to count, enhancing flexibility. - Updated the logic for counting library occurrences to utilize a more generic approach, accommodating any specified libraries. - Enhanced the Markdown output and console logging to reflect the new customizable library counts. - Revised README.md to document the new `--libraries` option for users. - Added a sample JSONL file for testing and updated tests to validate the new functionality.
1 parent 64ec0f2 commit 3fa3c63

File tree

5 files changed

+209
-87
lines changed

5 files changed

+209
-87
lines changed

README.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The input JSONL files are typically generated from Jarviz-lib static analysis of
1616
- Generate statistics about dependencies
1717
- View the most depended-upon classes
1818
- Extract and analyze package dependencies
19-
- Count instances of specific libraries (struts, commons, log4j, cryptix) in dependencies
19+
- Count instances of specific libraries (struts, commons, log4j, cryptix) in dependencies, customizable via `--libraries` option
2020

2121
## Installation
2222

@@ -58,15 +58,16 @@ The package dependencies extractor tool generates a Markdown report of all base
5858

5959
```bash
6060
# When installed locally
61-
npx ts-node package-dependencies.ts <jsonl-file-path> [--output <output-file-path>]
61+
npx ts-node package-dependencies.ts <jsonl-file-path> [--output <output-file-path>] [--libraries <libraries>]
6262

6363
# When installed globally
64-
java-dependency-mapper <jsonl-file-path> [--output <output-file-path>]
64+
java-dependency-mapper <jsonl-file-path> [--output <output-file-path>] [--libraries <libraries>]
6565
```
6666

6767
Where:
6868
- `<jsonl-file-path>` is the path to the JSONL file containing dependency data (required)
6969
- `--output` or `-o` followed by path where the Markdown output will be written (optional, defaults to `package-dependencies.md`)
70+
- `--libraries` or `-l` followed by a comma-separated list of libraries to count in dependencies (optional, defaults to `struts,commons,log4j,cryptix`)
7071

7172
Example usage:
7273
```bash
@@ -81,6 +82,12 @@ java-dependency-mapper sample-dependencies.jsonl --output reports/packages.md
8182

8283
# Using shorthand parameter
8384
java-dependency-mapper sample-dependencies.jsonl -o custom-output.md
85+
86+
# Specify custom libraries to count
87+
java-dependency-mapper sample-dependencies.jsonl --libraries "spring,hibernate,jetty"
88+
89+
# Using shorthand parameter for libraries
90+
java-dependency-mapper sample-dependencies.jsonl -l "log4j,slf4j,logback"
8491
```
8592

8693
## Development
@@ -166,10 +173,9 @@ No cycles found.
166173
The package dependencies extractor generates a Markdown file with the following sections:
167174

168175
1. **Specific Library Counts**: Tracks the number of dependencies where the targetClass contains specific libraries:
169-
- Struts
170-
- Commons
171-
- Log4j
172-
- Cryptix
176+
- By default: Struts, Commons, Log4j, Cryptix
177+
- Customizable via the `--libraries` option
178+
173179
This helps identify and quantify vulnerability exposure if any of these libraries have security issues.
174180

175181
2. **Base Packages**: A list of all base packages used by the project, grouped by:

package-dependencies.ts

Lines changed: 60 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@ interface PackageInfo {
2626

2727
// Interface for tracking specific library counts
2828
interface LibraryCounts {
29-
struts: number;
30-
commons: number;
31-
log4j: number;
32-
cryptix: number;
29+
[key: string]: number;
3330
}
3431

3532
class PackageDependencyExtractor {
@@ -38,12 +35,20 @@ class PackageDependencyExtractor {
3835
private artifactMap: Map<string, Set<string>> = new Map();
3936
private basePackageDependencyMap: Map<string, Set<string>> = new Map();
4037
// Add property to track library counts
41-
private libraryCounts: LibraryCounts = {
42-
struts: 0,
43-
commons: 0,
44-
log4j: 0,
45-
cryptix: 0
46-
};
38+
private libraryCounts: LibraryCounts = {};
39+
private librariesToCount: string[] = ['struts', 'commons', 'log4j', 'cryptix']; // Default libraries
40+
41+
// Constructor that allows setting libraries to count
42+
constructor(librariesToCount?: string) {
43+
if (librariesToCount) {
44+
this.librariesToCount = librariesToCount.split(',').map(lib => lib.trim().toLowerCase());
45+
}
46+
47+
// Initialize counter for each library
48+
this.librariesToCount.forEach(lib => {
49+
this.libraryCounts[lib] = 0;
50+
});
51+
}
4752

4853
async parseJsonlFile(filePath: string): Promise<void> {
4954
const fileStream = fs.createReadStream(filePath);
@@ -105,19 +110,14 @@ class PackageDependencyExtractor {
105110

106111
// Method to count instances of specific libraries
107112
private countSpecificLibraries(targetClass: string): void {
108-
// Check for each specific library in the targetClass
109-
if (targetClass.toLowerCase().includes('struts')) {
110-
this.libraryCounts.struts++;
111-
}
112-
if (targetClass.toLowerCase().includes('commons')) {
113-
this.libraryCounts.commons++;
114-
}
115-
if (targetClass.toLowerCase().includes('log4j')) {
116-
this.libraryCounts.log4j++;
117-
}
118-
if (targetClass.toLowerCase().includes('cryptix')) {
119-
this.libraryCounts.cryptix++;
120-
}
113+
const lcTargetClass = targetClass.toLowerCase();
114+
115+
// Check for each library in the target class
116+
this.librariesToCount.forEach(library => {
117+
if (lcTargetClass.includes(library)) {
118+
this.libraryCounts[library]++;
119+
}
120+
});
121121
}
122122

123123
private getPackageName(className: string): string {
@@ -226,10 +226,12 @@ class PackageDependencyExtractor {
226226
markdownContent += 'These counts represent the number of dependencies where the `targetClass` field in the JSONL data contains each specific library name. This helps quantify how many times your application code depends on classes from these libraries, which is useful for identifying vulnerability exposure.\n\n';
227227
markdownContent += '| Library | Count |\n';
228228
markdownContent += '|---------|-------|\n';
229-
markdownContent += `| Struts | ${this.libraryCounts.struts} |\n`;
230-
markdownContent += `| Commons | ${this.libraryCounts.commons} |\n`;
231-
markdownContent += `| Log4j | ${this.libraryCounts.log4j} |\n`;
232-
markdownContent += `| Cryptix | ${this.libraryCounts.cryptix} |\n\n`;
229+
230+
// Display counts for each library dynamically
231+
Object.keys(this.libraryCounts).forEach(library => {
232+
markdownContent += `| ${library.charAt(0).toUpperCase() + library.slice(1)} | ${this.libraryCounts[library]} |\n`;
233+
});
234+
markdownContent += '\n';
233235

234236
// List all base packages
235237
markdownContent += '## Base Packages\n\n';
@@ -326,16 +328,20 @@ class PackageDependencyExtractor {
326328

327329
// Log the library counts to console
328330
console.log('\nSpecific Library Counts:');
329-
console.log(`- Struts: ${this.libraryCounts.struts}`);
330-
console.log(`- Commons: ${this.libraryCounts.commons}`);
331-
console.log(`- Log4j: ${this.libraryCounts.log4j}`);
332-
console.log(`- Cryptix: ${this.libraryCounts.cryptix}`);
331+
Object.keys(this.libraryCounts).forEach(library => {
332+
console.log(`- ${library.charAt(0).toUpperCase() + library.slice(1)}: ${this.libraryCounts[library]}`);
333+
});
333334
}
334335

335336
// Getter for library counts (useful for testing)
336337
getLibraryCounts(): LibraryCounts {
337338
return this.libraryCounts;
338339
}
340+
341+
// Getter for libraries being counted
342+
getLibrariesToCount(): string[] {
343+
return [...this.librariesToCount];
344+
}
339345
}
340346

341347
async function main() {
@@ -344,6 +350,7 @@ async function main() {
344350
345351
Options:
346352
--output, -o <file> Specify output file path (default: package-dependencies.md)
353+
--libraries, -l <libs> Comma-separated list of libraries to count (default: struts,commons,log4j,cryptix)
347354
--help, -h Display this help information
348355
`;
349356

@@ -366,6 +373,7 @@ Options:
366373
// Parse command line arguments
367374
let jsonlFilePath = '';
368375
let outputFilePath = 'package-dependencies.md'; // Default output path
376+
let librariesToCount = ''; // Default is undefined, will use defaults in the constructor
369377

370378
for (let i = 0; i < args.length; i++) {
371379
if (args[i] === '--output' || args[i] === '-o') {
@@ -377,6 +385,15 @@ Options:
377385
console.error(usage);
378386
process.exit(1);
379387
}
388+
} else if (args[i] === '--libraries' || args[i] === '-l') {
389+
if (i + 1 < args.length) {
390+
librariesToCount = args[i + 1];
391+
i++; // Skip the next argument as we've already processed it
392+
} else {
393+
console.error('Error: Missing value for --libraries parameter');
394+
console.error(usage);
395+
process.exit(1);
396+
}
380397
} else if (!jsonlFilePath) {
381398
// The first non-flag argument is the input file
382399
jsonlFilePath = args[i];
@@ -411,13 +428,24 @@ Options:
411428
}
412429
}
413430

414-
const extractor = new PackageDependencyExtractor();
431+
// Create extractor with libraries to count (if specified)
432+
const extractor = new PackageDependencyExtractor(librariesToCount);
415433

416434
console.log(`Parsing dependencies from ${jsonlFilePath}...`);
417435
await extractor.parseJsonlFile(jsonlFilePath);
418436

419437
console.log(`Generating Markdown output to ${outputFilePath}...`);
420438
extractor.generateMarkdownOutput(outputFilePath);
439+
440+
// Log which libraries were counted
441+
console.log(`\nCounted the following libraries: ${extractor.getLibrariesToCount().join(', ')}`);
442+
443+
// Log the library counts to console
444+
console.log('\nSpecific Library Counts:');
445+
const libraryCounts = extractor.getLibraryCounts();
446+
Object.keys(libraryCounts).forEach(library => {
447+
console.log(`- ${library.charAt(0).toUpperCase() + library.slice(1)}: ${libraryCounts[library]}`);
448+
});
421449
}
422450

423451
main().catch(error => console.error('Error:', error));

sample-test.jsonl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{"appSetName":"SampleApp","applicationName":"SampleApp","artifactFileName":"sampleEJB-1.0.0.jar","artifactId":"sampleEJB","artifactGroup":"com.example.sample","artifactVersion":"1.0.0","sourceClass":"com.example.sample.component.servicelocator.ejb.ServiceLocator","sourceMethod":"<init>","targetClass":"org.apache.struts.actions.Action","targetMethod":"<init>"}
2+
{"appSetName":"SampleApp","applicationName":"SampleApp","artifactFileName":"sampleEJB-1.0.0.jar","artifactId":"sampleEJB","artifactGroup":"com.example.sample","artifactVersion":"1.0.0","sourceClass":"com.example.sample.component.servicelocator.ejb.ServiceLocator","sourceMethod":"<init>","targetClass":"org.apache.commons.lang.StringUtils","targetMethod":"<init>"}
3+
{"appSetName":"SampleApp","applicationName":"SampleApp","artifactFileName":"sampleEJB-1.0.0.jar","artifactId":"sampleEJB","artifactGroup":"com.example.sample","artifactVersion":"1.0.0","sourceClass":"com.example.sample.component.servicelocator.ejb.ServiceLocator","sourceMethod":"getBoolean","targetClass":"org.apache.log4j.Logger","targetMethod":"<init>"}
4+
{"appSetName":"SampleApp","applicationName":"SampleApp","artifactFileName":"sampleEJB-1.0.0.jar","artifactId":"sampleEJB","artifactGroup":"com.example.sample","artifactVersion":"1.0.0","sourceClass":"com.example.sample.component.servicelocator.ejb.ServiceLocator","sourceMethod":"getBoolean","targetClass":"cryptix.provider.Cipher","targetMethod":"booleanValue"}
5+
{"appSetName":"SampleApp","applicationName":"SampleApp","artifactFileName":"sampleEJB-1.0.0.jar","artifactId":"sampleEJB","artifactGroup":"com.example.sample","artifactVersion":"1.0.0","sourceClass":"com.example.sample.component.servicelocator.ejb.ServiceLocator","sourceMethod":"getDataSource","targetClass":"org.springframework.context.ApplicationContext","targetMethod":"<init>"}
6+
{"appSetName":"SampleApp","applicationName":"SampleApp","artifactFileName":"sampleEJB-1.0.0.jar","artifactId":"sampleEJB","artifactGroup":"com.example.sample","artifactVersion":"1.0.0","sourceClass":"com.example.sample.component.servicelocator.ejb.ServiceLocator","sourceMethod":"getLocalHome","targetClass":"org.hibernate.Session","targetMethod":"<init>"}

test-output.md

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,24 @@
22

33
This document lists all base packages that the project depends on.
44

5+
## Specific Library Counts
6+
7+
These counts represent the number of dependencies where the `targetClass` field in the JSONL data contains each specific library name. This helps quantify how many times your application code depends on classes from these libraries, which is useful for identifying vulnerability exposure.
8+
9+
| Library | Count |
10+
|---------|-------|
11+
| Spring | 1 |
12+
| Hibernate | 1 |
13+
| Jetty | 0 |
14+
515
## Base Packages
616

717
### External Dependencies
818

9-
- `java.lang`
19+
- `cryptix.provider`
20+
- `org.apache`
21+
- `org.hibernate`
22+
- `org.springframework`
1023

1124
### Internal Packages
1225

@@ -15,30 +28,67 @@ This document lists all base packages that the project depends on.
1528
## Dependency Relationships
1629

1730
- `com.example` depends on:
18-
- `java.lang`
31+
- `cryptix.provider`
32+
- `org.apache`
33+
- `org.hibernate`
34+
- `org.springframework`
1935

2036
## Package Details
2137

2238
### `com.example`
2339

2440
- **Type**: Internal Package
25-
- **Sub-packages**: 2
26-
- **Classes**: 2
27-
- **Dependencies**: `java.lang`
41+
- **Sub-packages**: 1
42+
- **Classes**: 1
43+
- **Dependencies**: `org.apache`, `cryptix.provider`, `org.springframework`, `org.hibernate`
2844

2945
Includes these sub-packages:
3046

31-
- `com.example.sample.component.servicelocator`
3247
- `com.example.sample.component.servicelocator.ejb`
3348

34-
### `java.lang`
49+
### `cryptix.provider`
50+
51+
- **Type**: External Dependency
52+
- **Sub-packages**: 1
53+
- **Classes**: 1
54+
- **Dependencies**: None
55+
56+
Includes these sub-packages:
57+
58+
- `cryptix.provider`
59+
60+
### `org.apache`
61+
62+
- **Type**: External Dependency
63+
- **Sub-packages**: 3
64+
- **Classes**: 3
65+
- **Dependencies**: None
66+
67+
Includes these sub-packages:
68+
69+
- `org.apache.commons.lang`
70+
- `org.apache.log4j`
71+
- `org.apache.struts.actions`
72+
73+
### `org.hibernate`
74+
75+
- **Type**: External Dependency
76+
- **Sub-packages**: 1
77+
- **Classes**: 1
78+
- **Dependencies**: None
79+
80+
Includes these sub-packages:
81+
82+
- `org.hibernate`
83+
84+
### `org.springframework`
3585

3686
- **Type**: External Dependency
3787
- **Sub-packages**: 1
38-
- **Classes**: 2
88+
- **Classes**: 1
3989
- **Dependencies**: None
4090

4191
Includes these sub-packages:
4292

43-
- `java.lang`
93+
- `org.springframework.context`
4494

0 commit comments

Comments
 (0)