Skip to content

Commit b242ddf

Browse files
authored
Merge pull request #120 from advanced-security/ljones140/fix-direct-when-self-referring
Fix Direct Dependencies Marked as Indirect
2 parents 04aaaf6 + 3349f8c commit b242ddf

File tree

5 files changed

+218
-73
lines changed

5 files changed

+218
-73
lines changed

componentDetection.test.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,114 @@ describe("ComponentDetection.makePackageUrl", () => {
6868
expect(packageUrl).toBe("");
6969
});
7070
});
71+
72+
describe("ComponentDetection.processComponentsToManifests", () => {
73+
test("adds package as direct dependency when no top level referrers", () => {
74+
const componentsFound = [
75+
{
76+
component: {
77+
name: "test-package",
78+
version: "1.0.0",
79+
packageUrl: {
80+
Scheme: "pkg",
81+
Type: "npm",
82+
Name: "test-package",
83+
Version: "1.0.0"
84+
},
85+
id: "test-package 1.0.0 - npm"
86+
},
87+
isDevelopmentDependency: false,
88+
topLevelReferrers: [], // Empty = direct dependency
89+
locationsFoundAt: ["package.json"]
90+
}
91+
];
92+
93+
const manifests = ComponentDetection.processComponentsToManifests(componentsFound);
94+
95+
expect(manifests).toHaveLength(1);
96+
expect(manifests[0].name).toBe("package.json");
97+
expect(manifests[0].directDependencies()).toHaveLength(1);
98+
expect(manifests[0].indirectDependencies()).toHaveLength(0);
99+
expect(manifests[0].countDependencies()).toBe(1);
100+
});
101+
102+
test("adds package as indirect dependency when has top level referrers", () => {
103+
const componentsFound = [
104+
{
105+
component: {
106+
name: "test-package",
107+
version: "1.0.0",
108+
packageUrl: {
109+
Scheme: "pkg",
110+
Type: "npm",
111+
Name: "test-package",
112+
Version: "1.0.0"
113+
},
114+
id: "test-package 1.0.0 - npm"
115+
},
116+
isDevelopmentDependency: false,
117+
topLevelReferrers: [
118+
{
119+
name: "parent-package",
120+
version: "1.0.0",
121+
packageUrl: {
122+
Scheme: "pkg",
123+
Type: "npm",
124+
Name: "parent-package",
125+
Version: "1.0.0"
126+
}
127+
}
128+
],
129+
locationsFoundAt: ["package.json"]
130+
}
131+
];
132+
133+
const manifests = ComponentDetection.processComponentsToManifests(componentsFound);
134+
135+
expect(manifests).toHaveLength(1);
136+
expect(manifests[0].name).toBe("package.json");
137+
expect(manifests[0].directDependencies()).toHaveLength(0);
138+
expect(manifests[0].indirectDependencies()).toHaveLength(1);
139+
expect(manifests[0].countDependencies()).toBe(1);
140+
});
141+
142+
test("adds package as direct dependency when top level referrer is itself", () => {
143+
const componentsFound = [
144+
{
145+
component: {
146+
name: "test-package",
147+
version: "1.0.0",
148+
packageUrl: {
149+
Scheme: "pkg",
150+
Type: "npm",
151+
Name: "test-package",
152+
Version: "1.0.0"
153+
},
154+
id: "test-package 1.0.0 - npm"
155+
},
156+
isDevelopmentDependency: false,
157+
topLevelReferrers: [
158+
{
159+
name: "test-package",
160+
version: "1.0.0",
161+
packageUrl: {
162+
Scheme: "pkg",
163+
Type: "npm",
164+
Name: "test-package",
165+
Version: "1.0.0"
166+
}
167+
}
168+
],
169+
locationsFoundAt: ["package.json"]
170+
}
171+
];
172+
173+
const manifests = ComponentDetection.processComponentsToManifests(componentsFound);
174+
175+
expect(manifests).toHaveLength(1);
176+
expect(manifests[0].name).toBe("package.json");
177+
expect(manifests[0].directDependencies()).toHaveLength(1);
178+
expect(manifests[0].indirectDependencies()).toHaveLength(0);
179+
expect(manifests[0].countDependencies()).toBe(1);
180+
});
181+
});

componentDetection.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,17 @@ export default class ComponentDetection {
6868

6969
public static async getManifestsFromResults(): Promise<Manifest[] | undefined> {
7070
core.info("Getting manifests from results");
71+
const results = await fs.readFileSync(this.outputPath, 'utf8');
72+
var json: any = JSON.parse(results);
73+
return this.processComponentsToManifests(json.componentsFound);
74+
}
75+
76+
public static processComponentsToManifests(componentsFound: any[]): Manifest[] {
7177
// Parse the result file and add the packages to the package cache
7278
const packageCache = new PackageCache();
7379
const packages: Array<ComponentDetectionPackage> = [];
7480

75-
const results = await fs.readFileSync(this.outputPath, 'utf8');
76-
77-
var json: any = JSON.parse(results);
78-
json.componentsFound.forEach(async (component: any) => {
81+
componentsFound.forEach(async (component: any) => {
7982
// Skip components without packageUrl
8083
if (!component.component.packageUrl) {
8184
core.debug(`Skipping component detected without packageUrl: ${JSON.stringify({
@@ -113,6 +116,7 @@ export default class ComponentDetection {
113116
}
114117

115118
const referrerUrl = ComponentDetection.makePackageUrl(referrer.packageUrl);
119+
referrer.packageUrlString = referrerUrl
116120

117121
// Skip if the generated packageUrl is empty
118122
if (!referrerUrl) {
@@ -135,20 +139,32 @@ export default class ComponentDetection {
135139
const manifests: Array<Manifest> = [];
136140

137141
// Check the locationsFoundAt for every package and add each as a manifest
138-
packages.forEach(async (pkg: ComponentDetectionPackage) => {
139-
pkg.locationsFoundAt.forEach(async (location: any) => {
142+
this.addPackagesToManifests(packages, manifests);
143+
144+
return manifests;
145+
}
146+
147+
private static addPackagesToManifests(packages: Array<ComponentDetectionPackage>, manifests: Array<Manifest>): void {
148+
packages.forEach((pkg: ComponentDetectionPackage) => {
149+
pkg.locationsFoundAt.forEach((location: any) => {
140150
if (!manifests.find((manifest: Manifest) => manifest.name == location)) {
141151
const manifest = new Manifest(location, location);
142152
manifests.push(manifest);
143153
}
144-
if (pkg.topLevelReferrers.length == 0) {
154+
155+
// Filter out self-references from topLevelReferrers
156+
const nonSelfReferrers = pkg.topLevelReferrers.filter((referrer: any) => {
157+
if (!referrer.packageUrlString) return false;
158+
return referrer.packageUrlString !== pkg.packageUrlString;
159+
});
160+
161+
if (nonSelfReferrers.length == 0) {
145162
manifests.find((manifest: Manifest) => manifest.name == location)?.addDirectDependency(pkg, ComponentDetection.getDependencyScope(pkg));
146163
} else {
147164
manifests.find((manifest: Manifest) => manifest.name == location)?.addIndirectDependency(pkg, ComponentDetection.getDependencyScope(pkg));
148165
}
149166
});
150167
});
151-
return manifests;
152168
}
153169

154170
private static getDependencyScope(pkg: ComponentDetectionPackage) {
@@ -236,10 +252,12 @@ export default class ComponentDetection {
236252
}
237253

238254
class ComponentDetectionPackage extends Package {
255+
public packageUrlString: string;
239256

240257
constructor(packageUrl: string, public id: string, public isDevelopmentDependency: boolean, public topLevelReferrers: [],
241258
public locationsFoundAt: [], public containerDetailIds: [], public containerLayerIds: []) {
242259
super(packageUrl);
260+
this.packageUrlString = packageUrl;
243261
}
244262
}
245263

dist/componentDetection.d.ts

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js

Lines changed: 78 additions & 64 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)