Skip to content

Commit a8c449e

Browse files
authored
feat: separate Red Hat and Community profiles for HTML report (#522)
* feat: separate Red Hat and Community profiles for HTML report * feat: updat general solution for backend * feat: add imageRecommendation refactor to programmatically get brandingConfig and update README * fix: simplify the logic fix: add imageRemediationLink fix: adapt HtmlReportTest by adding imageRecommend and imageRemediationLink to test configuration
1 parent 8cfb7be commit a8c449e

File tree

13 files changed

+1743
-1473
lines changed

13 files changed

+1743
-1473
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2023-2025 Trustify Dependency Analytics Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
*
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package io.github.guacsec.trustifyda.integration.report;
19+
20+
import io.smallrye.config.ConfigMapping;
21+
22+
@ConfigMapping(prefix = "branding")
23+
public interface BrandingConfig {
24+
String displayName();
25+
26+
String exploreUrl();
27+
28+
String exploreTitle();
29+
30+
String exploreDescription();
31+
32+
String imageRecommendation();
33+
34+
String imageRemediationLink();
35+
}

src/main/java/io/github/guacsec/trustifyda/integration/report/ReportTemplate.java

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import org.apache.camel.Body;
2727
import org.apache.camel.Exchange;
2828
import org.apache.camel.ExchangeProperty;
29+
import org.eclipse.microprofile.config.Config;
30+
import org.eclipse.microprofile.config.ConfigProvider;
2931
import org.eclipse.microprofile.config.inject.ConfigProperty;
3032

3133
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -74,6 +76,8 @@ public Map<String, Object> setVariables(
7476
params.put("cveIssueTemplate", cveIssuePathRegex);
7577
params.put("imageMapping", getImageMapping());
7678
params.put("rhdaSource", rhdaSource);
79+
getBrandingConfig()
80+
.ifPresent(config -> params.put("brandingConfig", getBrandingConfigMap(config)));
7781
if (!disabled && writeKey.isPresent()) {
7882
params.put("userId", userId);
7983
params.put("anonymousId", anonymousId);
@@ -108,6 +112,98 @@ private String getImageMapping() throws JsonProcessingException {
108112
return objectWriter.writeValueAsString(urlMapping);
109113
}
110114

115+
private Optional<BrandingConfig> getBrandingConfig() {
116+
try {
117+
Config config = ConfigProvider.getConfig();
118+
return config
119+
.getOptionalValue("branding.display-name", String.class)
120+
.filter(displayName -> !displayName.isEmpty())
121+
.map(
122+
displayName ->
123+
new BrandingConfigImpl(
124+
displayName,
125+
config.getOptionalValue("branding.explore-url", String.class).orElse(""),
126+
config.getOptionalValue("branding.explore-title", String.class).orElse(""),
127+
config
128+
.getOptionalValue("branding.explore-description", String.class)
129+
.orElse(""),
130+
config
131+
.getOptionalValue("branding.image-recommendation", String.class)
132+
.orElse(""),
133+
config
134+
.getOptionalValue("branding.image-remediation-link", String.class)
135+
.orElse("")));
136+
} catch (Exception e) {
137+
return Optional.empty();
138+
}
139+
}
140+
141+
private Map<String, String> getBrandingConfigMap(BrandingConfig config) {
142+
Map<String, String> branding = new HashMap<>();
143+
branding.put("displayName", config.displayName());
144+
branding.put("exploreUrl", config.exploreUrl());
145+
branding.put("exploreTitle", config.exploreTitle());
146+
branding.put("exploreDescription", config.exploreDescription());
147+
branding.put("imageRecommendation", config.imageRecommendation());
148+
branding.put("imageRemediationLink", config.imageRemediationLink());
149+
return branding;
150+
}
151+
152+
@RegisterForReflection
153+
private static class BrandingConfigImpl implements BrandingConfig {
154+
private final String displayName;
155+
private final String exploreUrl;
156+
private final String exploreTitle;
157+
private final String exploreDescription;
158+
private final String imageRecommendation;
159+
private final String imageRemediationLink;
160+
161+
public BrandingConfigImpl(
162+
String displayName,
163+
String exploreUrl,
164+
String exploreTitle,
165+
String exploreDescription,
166+
String imageRecommendation,
167+
String imageRemediationLink) {
168+
this.displayName = displayName;
169+
this.exploreUrl = exploreUrl;
170+
this.exploreTitle = exploreTitle;
171+
this.exploreDescription = exploreDescription;
172+
this.imageRecommendation = imageRecommendation;
173+
this.imageRemediationLink = imageRemediationLink;
174+
}
175+
176+
@Override
177+
public String displayName() {
178+
return displayName;
179+
}
180+
181+
@Override
182+
public String exploreUrl() {
183+
return exploreUrl;
184+
}
185+
186+
@Override
187+
public String exploreTitle() {
188+
return exploreTitle;
189+
}
190+
191+
@Override
192+
public String exploreDescription() {
193+
return exploreDescription;
194+
}
195+
196+
@Override
197+
public String imageRecommendation() {
198+
return imageRecommendation;
199+
}
200+
201+
@Override
202+
public String imageRemediationLink() {
203+
return imageRemediationLink;
204+
}
205+
}
206+
111207
@RegisterForReflection
112208
public static record IssueLinkFormatter(String issuePathRegex) {
113209

src/main/resources/application.properties

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,13 @@ quarkus.hibernate-orm.sql-load-script=no-file
6262
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://${db.postgres.host}:${db.postgres.port}/${db.postgres.database}?sslmode=${db.postgres.sslmode}
6363
%prod.quarkus.datasource.username=${db.postgres.user}
6464
%prod.quarkus.datasource.password=${db.postgres.password}
65+
66+
# ====================================================================
67+
# Branding Configuration Profiles
68+
# ====================================================================
69+
70+
# Default branding configuration (Community/Trustify) Example:
71+
#branding.display.name=Trustify
72+
#branding.explore.url=https://guac.sh/trustify/
73+
#branding.explore.title=Learn more about Trustify
74+
#branding.explore.description=The Trustify project is a collection of software components that enables you to store and retrieve Software Bill of Materials (SBOMs), and advisory documents.

src/main/resources/freemarker/templates/generated/main.js

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

src/main/resources/freemarker/templates/generated/vendor.js

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

src/test/java/io/github/guacsec/trustifyda/integration/HtmlReportTest.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,8 @@ public void testBatchHtmlWithToken() throws IOException {
263263
List<HtmlElement> sectionElements = rootElement.getByXPath("./section");
264264
assertEquals(1, sectionElements.size());
265265
List<HtmlAnchor> anchorElements =
266-
page.getByXPath(
267-
"//a[contains(@href, 'https://catalog.redhat.com/software/containers/ubi9/')]");
266+
page.getByXPath("//a[contains(@href, 'https://test-catalog.example.com/containers/ubi9')]");
268267
assertTrue(!anchorElements.isEmpty(), "At least one href contains the desired substring");
269-
270268
verifyTrustifyRequest(OK_TOKEN, 3);
271269
}
272270

src/test/resources/application.properties

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,11 @@ quarkus.hibernate-orm.sql-load-script=db/h2/V2__insert_data.sql
1515
provider.trustify.auth.server-url=${keycloak.url}/realms/trustify
1616
provider.trustify.auth.client-id=test-trustify-client
1717
provider.trustify.auth.client-secret=test-trustify-secret
18+
19+
# Branding configuration for testing
20+
branding.display-name=Trustify Test
21+
branding.explore-url=https://guac.sh/trustify/
22+
branding.explore-title=Learn more about Trustify
23+
branding.explore-description=The Trustify project is a collection of software components that enables you to store and retrieve Software Bill of Materials (SBOMs), and advisory documents.
24+
branding.image-recommendation=Test container image recommendations for enhanced security.
25+
branding.image-remediation-link=https://test-catalog.example.com/containers/

ui/BRANDING.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Branding Configuration
2+
3+
This project supports flexible branding through backend environment variables. The system uses **default** branding and allows you to override specific elements as needed.
4+
5+
## Profile Configuration
6+
7+
The branding profiles are defined in `src/main/resources/application.properties`:
8+
9+
```properties
10+
# Default branding configuration (Community/Trustify)
11+
branding.display.name=Trustify
12+
branding.explore.url=https://guac.sh/trustify/
13+
branding.explore.title=Learn more about Trustify
14+
branding.explore.description=The Trustify project is a collection of software components that enables you to store and retrieve Software Bill of Materials (SBOMs), and advisory documents.
15+
branding.image.recommendation=
16+
branding.image.remediation.link=
17+
18+
# Example: Custom organization branding profile
19+
%myorg.branding.display.name=MyOrg
20+
%myorg.branding.explore.url=https://example.com/security-tools
21+
%myorg.branding.explore.title=Learn about MyOrg Security
22+
%myorg.branding.explore.description=Explore our comprehensive security analysis tools and vulnerability management platform.
23+
%myorg.branding.image.recommendation=Custom container image recommendations for enhanced security.
24+
%myorg.branding.image.remediation.link=https://example.com/container-catalog
25+
```
26+
27+
## Examples
28+
29+
### Method 1: Using Predefined Profile
30+
31+
To create a new branding profile for your organization:
32+
33+
1. **Add profile configuration** to `src/main/resources/application.properties`:
34+
```properties
35+
# Replace 'myorg' with your organization identifier
36+
%myorg.branding.display.name=Your Organization Name
37+
%myorg.branding.explore.url=https://your-org.com/security
38+
%myorg.branding.explore.title=Learn about Your Org Security
39+
%myorg.branding.explore.description=Your custom description here.
40+
%myorg.branding.image.recommendation=Your custom container image recommendation text.
41+
%myorg.branding.image.remediation.link=https://your-org.com/container-catalog
42+
```
43+
44+
2. **Use the profile** when running the application:
45+
```bash
46+
./mvnw quarkus:dev -Dquarkus.profile=myorg
47+
```
48+
49+
3. **Override icons in private projects** using CSS to provide custom organization icons
50+
51+
### Method 2: Using Environment Variables
52+
53+
**Custom Organization Configuration:**
54+
```bash
55+
export BRANDING_DISPLAY_NAME="MyOrg"
56+
export BRANDING_EXPLORE_URL="https://example.com/security-tools"
57+
export BRANDING_EXPLORE_TITLE="Learn about MyOrg Security"
58+
export BRANDING_EXPLORE_DESCRIPTION="Explore our comprehensive security analysis tools and vulnerability management platform."
59+
export BRANDING_IMAGE_RECOMMENDATION="Custom container image recommendations for enhanced security."
60+
export BRANDING_IMAGE_REMEDIATION_LINK="https://example.com/container-catalog"
61+
62+
./mvnw quarkus:dev
63+
```
64+
65+
Environment variables will override profile settings if both are provided.
66+
67+
## Custom Icon Implementation
68+
69+
To implement a completely custom icon for your organization, follow this step-by-step guide:
70+
71+
**Step 1: Add your custom icon**
72+
```bash
73+
# Copy your organization's icon to the UI assets directory
74+
cp /path/to/myorg.png /path/to/trustify-dependency-analytics/ui/src/images/myorg.png
75+
```
76+
77+
**Step 2: Create CSS override**
78+
Add the following CSS to `/ui/src/index.css`:
79+
```css
80+
/* Custom icon override - Replace Trustify icon with MyOrg icon */
81+
img[alt="My Org Icon"] {
82+
content: url('./images/myorg.png') !important;
83+
width: 16px !important;
84+
height: 16px !important;
85+
}
86+
```
87+
88+
**Step 3: Configure organization branding**
89+
Add to `/src/main/resources/application.properties`:
90+
```properties
91+
# MyOrg custom branding profile
92+
%myorg.branding.display.name=MyOrg
93+
%myorg.branding.explore.url=https://myorg.com/security
94+
%myorg.branding.explore.title=Learn about MyOrg Security
95+
%myorg.branding.explore.description=Explore our comprehensive security analysis tools and vulnerability management platform.
96+
%myorg.branding.image.recommendation=Custom container image recommendations for enhanced security.
97+
%myorg.branding.image.remediation.link=https://myorg.com/container-catalog
98+
```
99+
100+
**Step 4: Build with your changes**
101+
```bash
102+
cd ui && npm run build
103+
```
104+
105+
**Step 5: Run with your branding**
106+
```bash
107+
./mvnw quarkus:dev -Dquarkus.profile=myorg
108+
```

ui/src/api/report.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ export interface AppData {
1010
anonymousId?: string | null;
1111
writeKey?: string | null;
1212
rhdaSource?: string | null;
13+
brandingConfig?: BrandingConfig;
14+
}
15+
16+
export interface BrandingConfig {
17+
displayName: string;
18+
exploreUrl: string;
19+
exploreTitle: string;
20+
exploreDescription: string;
21+
imageRecommendation: string;
22+
imageRemediationLink: string;
1323
}
1424

1525
export interface ReportMap {

0 commit comments

Comments
 (0)