Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
# spring-boot-dependency-checker

A small utility that finds manually overridden dependencies in a Maven POM, Gradle file, or SBOM for a Spring Boot application.
A small utility that finds manually overridden dependencies in a Maven POM, or Gradle file for a Spring Boot application.

## Usage

`npm install -g spring-boot-dependency-checker`

`spring-boot-dependency-checker location/to/pom.xml`

| File type | Dependencies | Properties | Accurate |
|----------------|--------------|------------|----------|
| Maven POM | ✓ | ✓ | ✓ |
| `build.gradle` | ✓ | ✗ | ✓ |
| SBOM | ✓ | ✗ | ✗ |
| File type | Dependencies | Properties |
|----------------|--------------|------------|
| Maven POM | ✓ | ✓ |
| `build.gradle` | ✓ | ✗ |

Maven POM is the most accurate because it's generated from the source file

build.gradle does not support overwritten properties because those usually come from a separate file

SBOM is accurate until you have dependencies that pull in newer versions than what Spring Boot recommends, which results in false positives
15 changes: 9 additions & 6 deletions gradle.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,25 @@ export const retrieveSimilarGradlePackages = async (parsedGradle, springBootVers
const defaultVersions = await getDefaultSpringBootVersions(springBootVersion);

if (defaultVersions.length) {
const mismatchedPackages = [];
const declaredPackages = [];
gradleDependenciesWithVersions.forEach(gradleDependency => defaultVersions.forEach(bootPackage => {
if (gradleDependency.group === bootPackage.group && gradleDependency.name === bootPackage.name) {
const existingMatches = mismatchedPackages.find(mismatchedPackage => mismatchedPackage.group === gradleDependency.group && mismatchedPackage.name === gradleDependency.name);
const existingMatches = declaredPackages.find(declaredPackage => declaredPackage.group === gradleDependency.group && declaredPackage.name === gradleDependency.name);
if (!existingMatches) {
mismatchedPackages.push(new Package(gradleDependency.group, gradleDependency.name, gradleDependency.version, bootPackage.version));
declaredPackages.push(new Package(gradleDependency.group, gradleDependency.name, gradleDependency.version, bootPackage.version));
}
}
}));

console.log('Mismatched Gradle Package Count -', mismatchedPackages.length);
if (mismatchedPackages.length) {
console.log('Mismatched Gradle Packages -', mismatchedPackages);
console.log('Declared Gradle Package Count -', declaredPackages.length);
if (declaredPackages.length) {
console.log('Declared Gradle Packages -', declaredPackages);
}
return declaredPackages;
} else {
console.log('Spring Boot default versions URL no longer exists.');
return [];
}
}
return [];
};
14 changes: 6 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { extname } from 'path';
import { retrieveSimilarSbomPackages } from './sbom.js';
import {
getPomSpringBootVersion,
getXMLFromFile,
Expand All @@ -13,26 +12,25 @@ export const checkDependencies = async () => {
const filename = process.argv[2];
const fileExtension = extname(filename);
// console.log('filename', filename, 'fileExtension', fileExtension);
if (fileExtension === '.json') {
console.log('Processing json file');
await retrieveSimilarSbomPackages(filename);
} else if (fileExtension === '.xml') {
if (fileExtension === '.xml') {
console.log('Processing xml file');
const parsedPom = await getXMLFromFile(filename);
const springBootVersion = await getPomSpringBootVersion(parsedPom);
if (springBootVersion) {
console.log('Detected Spring Boot Version -', springBootVersion);
await retrieveSimilarPomPackages(parsedPom, springBootVersion);
await retrieveSimilarPomProperties(parsedPom, springBootVersion);
const declaredPackages = await retrieveSimilarPomPackages(parsedPom, springBootVersion);
const declaredProperties = await retrieveSimilarPomProperties(parsedPom, springBootVersion);
return { packages: declaredPackages, properties: declaredProperties };
}
} else if (fileExtension === '.gradle') {
console.log('Processing gradle file');
const parsedGradle = await getJSFromFile(filename);
const springBootVersion = await getGradleSpringBootVersion(parsedGradle);
if (springBootVersion) {
console.log('Detected Spring Boot Version -', springBootVersion);
await retrieveSimilarGradlePackages(parsedGradle, springBootVersion);
const declaredPackages = await retrieveSimilarGradlePackages(parsedGradle, springBootVersion);
// await retrieveSimilarPomProperties(parsedPom, springBootVersion);
return { packages: declaredPackages, properties: [] };
}
} else {
console.log('Unknown extension, unable to process file.');
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "spring-boot-dependency-checker",
"version": "0.1.2",
"version": "0.1.3",
"description": "Spring Boot Dependency Checker - validate that you're using the versions Spring Boot has approved with your project.",
"keywords": [
"spring boot",
Expand Down
27 changes: 21 additions & 6 deletions pom.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ export const getPomSpringBootVersion = async (parsedPom) => {
if (parsedPom.project?.parent?.groupId === 'org.springframework.boot' && parsedPom.project?.parent?.artifactId === 'spring-boot-starter-parent') {
return parsedPom.project.parent.version;
}
if (parsedPom.project?.dependencyManagement?.dependencies?.dependency?.groupId === 'org.springframework.boot' && parsedPom.project?.dependencyManagement?.dependencies?.dependency?.artifactId === 'spring-boot-dependencies') {
const bootVersion = parsedPom.project.dependencyManagement.dependencies.dependency.version;
// version is in a variable
if (bootVersion.startsWith('${')) {
const variableName = bootVersion.replace('${', '').replace('}', '');
return parsedPom.project.properties[variableName];
}
return bootVersion;
}
console.log('No Spring Boot version found.');
return '';
};
Expand All @@ -48,24 +57,27 @@ export const retrieveSimilarPomPackages = async (parsedPom, springBootVersion) =
const defaultVersions = await getDefaultSpringBootVersions(springBootVersion);

if (defaultVersions.length) {
const mismatchedPackages = [];
const declaredPackages = [];
pomDependenciesWithVersions.forEach(pomDependency => defaultVersions.forEach(bootPackage => {
if (pomDependency.groupId === bootPackage.group && pomDependency.artifactId === bootPackage.name) {
const existingMatches = mismatchedPackages.find(mismatchedPackage => mismatchedPackage.group === pomDependency.groupId && mismatchedPackage.name === pomDependency.artifactId);
const existingMatches = declaredPackages.find(declaredPackage => declaredPackage.group === pomDependency.groupId && declaredPackage.name === pomDependency.artifactId);
if (!existingMatches) {
mismatchedPackages.push(new Package(pomDependency.groupId, pomDependency.artifactId, pomDependency.version, bootPackage.version));
declaredPackages.push(new Package(pomDependency.groupId, pomDependency.artifactId, pomDependency.version, bootPackage.version));
}
}
}));

console.log('Mismatched Pom Package Count -', mismatchedPackages.length);
if (mismatchedPackages.length) {
console.log('Mismatched Pom Packages -', mismatchedPackages);
console.log('Declared Pom Package Count -', declaredPackages.length);
if (declaredPackages.length) {
console.log('Declared Pom Packages -', declaredPackages);
}
return declaredPackages
} else {
console.log('Spring Boot default versions URL no longer exists.');
return [];
}
}
return [];
};

export const retrieveSimilarPomProperties = async (parsedPom, springBootVersion) => {
Expand All @@ -85,10 +97,13 @@ export const retrieveSimilarPomProperties = async (parsedPom, springBootVersion)
if (declaredProperties.length) {
console.log('Declared Pom Properties -', declaredProperties);
}
return declaredProperties;
} else {
console.log('Spring Boot default versions URL no longer exists.');
return [];
}
}
return [];
};

const getSpringDefaultProperties = async (sbVersion) => {
Expand Down
46 changes: 0 additions & 46 deletions sbom.js

This file was deleted.

53 changes: 0 additions & 53 deletions test/test-index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { strictEqual } from 'node:assert';
import { writeFileSync } from 'fs';
import { unlink } from 'node:fs';
import { getJsonFromFile } from '../shared.js';
import { getComponents, getSpringBootVersion } from '../sbom.js';

describe('test file reading', () => {
const filename = 'testFile.json';
Expand All @@ -22,61 +21,9 @@ describe('test file reading', () => {
strictEqual(jsonData.components[0].version, '3.1.1');
});

it('should retrieve components when they exist', async () => {
const testFile = {
components: [{
'name': 'new-name', 'version': '7.1.19',
}],
};
await writeFileSync(filename, JSON.stringify(testFile, null, 2));

const jsonData = await getComponents(filename);


strictEqual(jsonData[0].name, 'new-name');
strictEqual(jsonData[0].version, '7.1.19');
});

it('should gracefully exit when components do not exist', async () => {
const testFile = {
bananas: [{
'name': 'new-name', 'version': '7.1.19',
}],
};
await writeFileSync(filename, JSON.stringify(testFile, null, 2));

const jsonData = await getComponents(filename);

strictEqual(jsonData, undefined);
});

after(() => {
unlink(filename, (err) => {
if (err) throw err;
});
});
});

describe('test getSpringBootVersion', () => {
it('should get spring boot with group, name, and version', async () => {
const components = [{
'group': 'org.springframework.boot', 'name': 'spring-boot', 'version': '3.1.9',
}];
const springBootVersion = await getSpringBootVersion(components);
strictEqual(springBootVersion, '3.1.9');
});

it('should get spring boot with only name and version', async () => {
const components = [{
'name': 'spring-boot', 'version': '3.1.1',
}];
const springBootVersion = await getSpringBootVersion(components);
strictEqual(springBootVersion, '3.1.1');
});

it('should return empty string when spring boot version is not found', async () => {
const components = [{}];
const springBootVersion = await getSpringBootVersion(components);
strictEqual(springBootVersion, '');
});
});