Skip to content

Commit

Permalink
Merge pull request #521 from RedHatProductSecurity/feature/OSIDB-3742…
Browse files Browse the repository at this point in the history
…-cwe-api-service

✨ OSIDB-3480: CWE api service implementation
  • Loading branch information
C-Valen authored Jan 15, 2025
2 parents 67c0a40 + 0af8d76 commit 9b2131d
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 4 deletions.
4 changes: 3 additions & 1 deletion build/entrypoint.d/20-osim-runtime-json.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ OSIM_BACKENDS_OSIDB_AUTH="${OSIM_BACKENDS_OSIDB_AUTH:-kerberos}"
OSIM_BACKENDS_BUGZILLA="${OSIM_BACKENDS_BUGZILLA:-http://bugzilla-service:8001}"
OSIM_BACKENDS_JIRA="${OSIM_BACKENDS_JIRA:-http://jira-service:8002}"
OSIM_BACKENDS_ERRATA="${OSIM_BACKENDS_ERRATA:-http://errata-service:8003}"
OSIM_BACKENDS_MITRE="${OSIM_BACKENDS_MITRE:-http://mitre-service:8003}"
OSIM_BACKENDS_JIRA_DISPLAY="${OSIM_BACKENDS_JIRA_DISPLAY:-http://jira-service:8002}"
OSIM_VERSION='{"rev":"dev","tag":"dev","timestamp":"1970-01-01T00:00:00Z"}'
OSIM_READONLY_MODE=${OSIM_READONLY_MODE:-false}
Expand All @@ -33,7 +34,8 @@ IFS= read -r -d '' OSIM_RUNTIME <<EOF || :
"bugzilla": "${OSIM_BACKENDS_BUGZILLA}",
"jira": "${OSIM_BACKENDS_JIRA}",
"errata": "${OSIM_BACKENDS_ERRATA}",
"jiraDisplay": "${OSIM_BACKENDS_JIRA_DISPLAY}"
"jiraDisplay": "${OSIM_BACKENDS_JIRA_DISPLAY}",
"mitre": "${OSIM_BACKENDS_MITRE}"
},
"osimVersion": ${OSIM_VERSION},
"readOnly": ${OSIM_READONLY_MODE}
Expand Down
23 changes: 23 additions & 0 deletions build/entrypoint.d/32-mitre-proxy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/sh

# Skip creating proxy file if no proxies are configured
if [ -z "$OSIM_NGINX_PROXY_MITRE" ]; then
exit 0;
fi

# Get proxy certificate
curl --xattr "$OSIM_NGINX_PROXY_CA" -o /tmp/Proxy-CA.crt

# Ensure no trailing slash in variable, so no duplicate trailing slash is added in proxy_pass
OSIM_NGINX_PROXY_MITRE="${OSIM_NGINX_PROXY_MITRE%/}"

echo resolver "$(awk -v ORS=' ' '$1=="nameserver" {print $2}' /etc/resolv.conf)" ";" >/etc/nginx/conf.d/resolvers.conf

# Add MITRE reverse proxy endpoint
cat <<EOF >/tmp/osim-nginx-proxy.conf
location /proxy/mitre/ {
# Trailing slash in proxy_pass strips the location directive prefix from the downstream URL
proxy_pass ${OSIM_NGINX_PROXY_MITRE}/;
proxy_http_version 1.1;
}
EOF
11 changes: 9 additions & 2 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ OSIM_BACKENDS_BUGZILLA="http://bugzilla-service:8001"
OSIM_BACKENDS_JIRA="http://jira-service:8002"
OSIM_BACKENDS_ERRATA="http://errata-service:8003"
OSIM_BACKENDS_JIRA_DISPLAY="http://jira-service:8002"
OSIM_BACKENDS_MITRE="http://mitre-service:8084"
OSIM_NGINX_PROXY_MITRE=
OSIM_NGINX_PROXY_JIRA=
OSIM_NGINX_PROXY_CA=
```
Expand All @@ -91,6 +93,10 @@ OSIM_NGINX_PROXY_CA=
server, to simplify CORS configuration. `OSIM_BACKENDS_JIRA` should be updated
to point to OSIM's proxy. It might be required to also add the `OSIM_NGINX_PROXY_CA` with the certificate's endpoint for the proxy authorization.

* `OSIM_NGINX_PROXY_MITRE`: If set, creates a /proxy/mitre/ endpoint in the OSIM
server, to simplify CORS configuration. `OSIM_BACKENDS_MITRE` should be updated
to point to OSIM's proxy.

## Local Configuration

* Create `public/runtime.json` with the following contents:
Expand All @@ -102,8 +108,9 @@ OSIM_NGINX_PROXY_CA=
"osidbAuth": "<kerberos|credentials>",
"bugzilla": "http://localhost:8001",
"jira": "http://localhost:8002",
"errata": "http://localhost:8003"
"jiraDisplay": "http://localhost:8002"
"errata": "http://localhost:8003",
"jiraDisplay": "http://localhost:8002",
"mitre": "https://localhost:8004"
},
"osimVersion": {
"rev":"dev",
Expand Down
3 changes: 3 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ import {
} from '@/stores/osimRuntime';
import { footerHeight, footerTop } from '@/stores/responsive';
import { updateCWEData } from './services/CweService';
setup();
watch(osimRuntimeStatus, () => {
if (osimRuntimeStatus.value === OsimRuntimeStatus.READY) {
updateRelativeOsimBuildDate();
updateCWEData();
}
});
Expand Down
88 changes: 88 additions & 0 deletions src/services/CweService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { osimRuntime } from '@/stores/osimRuntime';
import type { CWEMemberType } from '@/types/mitreCwe';

const DATA_KEY = 'CWE:API-DATA';
const VERSION_KEY = 'CWE:API-VERSION';

interface CweViews {
Views: {
Members: {
CweID: string;
}[];
}[];
}

interface CweCategories {
Categories: {
ID: string;
Name: string;
}[];
}

export async function updateCWEData() {
const baseUrl = osimRuntime.value.backends.mitre;
try {
if (!baseUrl) {
console.debug('No Mitre backed configured, skipping CWE API cache update.');
return;
}

const [version, isNew] = await checkNewVersion(baseUrl);
if (!isNew) {
console.debug('✅ CWE API cache is up-to-date.');
return;
}

fetchAndCache(baseUrl);

localStorage.setItem(VERSION_KEY, version);
} catch (error) {
console.error('CweService::fetchAndCacheAPI() Error getting CWE data', error);
}
}

async function checkNewVersion(baseUrl: string): Promise<[string, boolean]> {
const version = await fetch(`${baseUrl}/cwe/version`);

if (!version.ok) {
throw new Error('Failed to fetch CWE version data');
}

const versionData = await version.json();
const storedVersion = localStorage.getItem(VERSION_KEY);

const isNew = storedVersion !== versionData.ContentVersion;
return [versionData.ContentVersion, isNew];
}

async function fetchAndCache(baseUrl: string) {
const cweIds = await fetchCweIds(baseUrl);
const cweData = await fetchCweNames(baseUrl, cweIds);
localStorage.setItem(DATA_KEY, JSON.stringify(cweData));
console.debug('✅ CWE API cache updated.');
}

async function fetchCweIds(baseUrl: string) {
// 699 is the id for the "Software Development" CWE view
const view = await fetch(`${baseUrl}/cwe/view/699`);
if (!view.ok) {
throw new Error('Failed to fetch CWE OpenAPI data');
}
const cweData: CweViews = await view.json();
const cweIds = cweData.Views[0].Members.map(member => member.CweID);
return cweIds;
}

async function fetchCweNames(baseUrl: string, cweIds: string[]) {
const detailedResponse = await fetch(`${baseUrl}/cwe/category/${cweIds.join(',')}`);
if (!detailedResponse.ok) {
throw new Error('Failed to fetch detailed CWE data');
}

const detailedData: CweCategories = await detailedResponse.json();
const customStructure: CWEMemberType[] = detailedData.Categories.map(category => ({
id: category.ID,
name: category.Name,
}));
return customStructure;
}
3 changes: 2 additions & 1 deletion src/stores/UserStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ export const useUserStore = defineStore('UserStore', () => {
.then((json) => {
const parsedWhoamiResponse = whoamiResponse.parse(json);
_userStoreSession.value.whoami = parsedWhoamiResponse;
}).then(async () => {
})
.then(async () => {
await updateJiraUsername();
})
.catch((e) => {
Expand Down
1 change: 1 addition & 0 deletions src/stores/__tests__/osimRuntime.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('osimRuntime', () => {
bugzilla: 'bugzilla',
jira: 'jira',
errata: 'errata',
mitre: 'mitre',
jiraDisplay: 'jiraDisplay',
osidbAuth: 'credentials',
},
Expand Down
2 changes: 2 additions & 0 deletions src/stores/osimRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const OsimRuntime = z.object({
jira: z.string(),
errata: z.string(),
jiraDisplay: z.string(),
mitre: z.string(),
}),
osimVersion: z.object({
rev: z.string(),
Expand All @@ -52,6 +53,7 @@ const runtime = ref<OsimRuntime>({
jira: 'http://jira-service:8002',
errata: 'http://errata-service:8003',
jiraDisplay: 'http://jira-service:8002',
mitre: 'http://mitre-service:8004',
},
osimVersion: { rev: 'dev', tag: 'dev', timestamp: '1970-01-01T00:00:00Z' },
error: 'error',
Expand Down
4 changes: 4 additions & 0 deletions src/types/mitreCwe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface CWEMemberType {
id: string;
name: string;
}
7 changes: 7 additions & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ export default defineConfig(({ mode }) => ({
},
server: {
https: true,
proxy: {
'/proxy/mitre': {
target: 'https://cwe-api.mitre.org/api/v1',
changeOrigin: true,
rewrite: path => path.replace(/^\/proxy\/mitre/, ''),
},
},
},
resolve: {
alias: {
Expand Down

0 comments on commit 9b2131d

Please sign in to comment.