diff --git a/allowedSnakeCase.cjs b/allowedSnakeCase.cjs
index b257daf6f7..8bc6e4a50e 100644
--- a/allowedSnakeCase.cjs
+++ b/allowedSnakeCase.cjs
@@ -29,6 +29,8 @@ module.exports = [
'_asset_id',
'asset_id',
'assigned_to',
+ 'audit_report',
+ 'audit_reports',
'auth_algorithm',
'auth_conf_setting',
'auth_method',
@@ -248,6 +250,7 @@ module.exports = [
'highest_severity',
'high_per_host',
'host_allow',
+ 'host_compliance',
'host_cves',
'hostnames_by_ip',
'hosts_allow',
diff --git a/public/locales/gsa-de.json b/public/locales/gsa-de.json
index 24374558ee..a302ea7032 100644
--- a/public/locales/gsa-de.json
+++ b/public/locales/gsa-de.json
@@ -158,6 +158,9 @@
"Audit made visible for:\n{{user}}\n{{role}}\n{{group}}": "Audit sichtbar gemacht für:\n{{user}}\n{{role}}\n{{group}}",
"Audit: {{name}}": "Audit: {{name}}",
"Audits": "Audits",
+ "Audit Reports by Compliance (Total: {{count}})": "Audit-Berichte nach Compliance (Gesamt: {{count}})",
+ "Audit Reports": "Audit-Berichte",
+ "Audit Report": "Audit-Bericht",
"Audits using this Policy": "Audits, die diese Richtlinie verwenden",
"Auth": "Auth.",
"Auth Algorithm": "Auth-Algorithmus",
@@ -243,6 +246,7 @@
"Certificate in use will expire at {{date}}": "Aktuelles Zertifikat wird am {{date}} ablaufen",
"Change Password": "Passwort ändern",
"Changed": "Verändert",
+ "Chart: Audit Reports by Compliance": "Diagramm: Audit-Berichte nach Compliance",
"Chart: CERT-Bund Advisories by CVSS": "Diagramm: CERT-Bund-Advisories nach CVSS",
"Chart: CERT-Bund Advisories by Creation Time": "Diagramm: CERT-Bund-Advisories nach Erstellungszeit",
"Chart: CERT-Bund Advisories by Severity Class": "Diagramm: CERT-Bund-Advisories nach Schweregradklasse",
@@ -323,6 +327,8 @@
"Complete": "Vollständig",
"Complexity": "Komplexität",
"Compliance Audits": "Compliance Audits",
+ "Compliance Audit Reports": "Compliance-Audit-Berichte",
+ "Compliance Percent": "Compliance-Prozent",
"Compliance Policies": "Compliance Richtlinien",
"Compliance Status": "Compliance Status",
"Compose": "Zusammenstellen",
@@ -841,6 +847,7 @@
"Include log messages in your filter settings.": "Log-Nachrichten in die Filtereinstellungen einbeziehen.",
"Include report": "Bericht einfügen",
"Included": "Beinhaltet",
+ "Incomplete": "Unvollständig",
"Info": "Info",
"Information": "Informationen",
"Inheriting user": "Erbender Benutzer",
@@ -1543,6 +1550,7 @@
"TLS Certificates by Modification Time (Total: {{count}})": "TLS-Zertifikate nach Änderungszeit (Gesamt: {{count}})",
"TLS Certificates by Status (Total: {{count}})": "TLS-Zertifikate nach Status (Gesamt: {{count}})",
"TLS Certificates for this Host": "TLS-Zertifikate für diesen Host",
+ "Table: Audit Reports by Compliance": "Tabelle: Audit-Berichte nach Compliance",
"Table: CERT-Bund Advisories by CVSS": "Tabelle: CERT-Bund-Advisories nach CVSS",
"Table: CERT-Bund Advisories by Creation Time": "Tabelle: CERT-Bund-Advisories nach Erstellungszeit",
"Table: CERT-Bund Advisories by Severity Class": "Tabelle: CERT-Bund-Advisories nach Schweregradklasse",
@@ -1908,6 +1916,7 @@
"task": "Aufgabe",
"to": "für",
"undefined": "undefiniert",
+ "Undefined": "Undefiniert",
"until {{- enddate}}": "bis {{- enddate}}",
"verinice Connector": "verinice-Konnektor",
"verinice.PRO Connector": "verinice.PRO-Konnektor",
diff --git a/src/gmp/capabilities/capabilities.js b/src/gmp/capabilities/capabilities.js
index f8baa0e991..297f9e8fc9 100644
--- a/src/gmp/capabilities/capabilities.js
+++ b/src/gmp/capabilities/capabilities.js
@@ -9,8 +9,8 @@ import {pluralizeType} from 'gmp/utils/entitytype';
import {parseBoolean} from 'gmp/parser';
const types = {
- audit: 'task',
- audits: 'task',
+ auditreport: 'audit_report',
+ auditreports: 'audit_reports',
host: 'asset',
hosts: 'asset',
os: 'asset',
@@ -42,12 +42,19 @@ const types = {
tlscertificates: 'tls_certificate',
};
+const subtypes = {
+ audit: 'task',
+ audits: 'task',
+ audit_report: 'report',
+ audit_reports: 'reports',
+};
+
const convertType = type => {
const ctype = types[type];
if (isDefined(ctype)) {
- return ctype;
+ type = ctype;
}
- return type;
+ return subtypes[type] || type;
};
class Capabilities {
diff --git a/src/gmp/commands/__tests__/auditreport.js b/src/gmp/commands/__tests__/auditreport.js
new file mode 100644
index 0000000000..d6eef9ec28
--- /dev/null
+++ b/src/gmp/commands/__tests__/auditreport.js
@@ -0,0 +1,33 @@
+/* SPDX-FileCopyrightText: 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import {describe, test, expect} from '@gsa/testing';
+import {createHttp, createEntityResponse} from 'gmp/commands/testing';
+import {AuditReportCommand} from 'gmp/commands/auditreports';
+
+describe('AuditReportCommand tests', () => {
+ test('should request single audit report', () => {
+ const response = createEntityResponse('report', {_id: 'foo'});
+ const fakeHttp = createHttp(response);
+
+ expect.hasAssertions();
+
+ const cmd = new AuditReportCommand(fakeHttp);
+ return cmd.get({id: 'foo'}).then(resp => {
+ expect(fakeHttp.request).toHaveBeenCalledWith('get', {
+ args: {
+ cmd: 'get_report',
+ report_id: 'foo',
+ ignore_pagination: 1,
+ details: 1,
+ lean: 1,
+ },
+ });
+
+ const {data} = resp;
+ expect(data.id).toEqual('foo');
+ });
+ });
+});
diff --git a/src/gmp/commands/__tests__/auditreports.js b/src/gmp/commands/__tests__/auditreports.js
new file mode 100644
index 0000000000..1169cfd917
--- /dev/null
+++ b/src/gmp/commands/__tests__/auditreports.js
@@ -0,0 +1,92 @@
+/* SPDX-FileCopyrightText: 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import {describe, test, expect} from '@gsa/testing';
+import {ALL_FILTER} from 'gmp/models/filter';
+
+import {
+ createHttp,
+ createEntitiesResponse,
+ createAggregatesResponse,
+} from '../testing';
+import {AuditReportsCommand} from 'gmp/commands/auditreports';
+
+describe('AuditReportsCommand tests', () => {
+ test('should return all audit reports', () => {
+ const response = createEntitiesResponse('report', [
+ {
+ _id: '1',
+ },
+ {
+ _id: '2',
+ },
+ ]);
+
+ const fakeHttp = createHttp(response);
+
+ expect.hasAssertions();
+
+ const cmd = new AuditReportsCommand(fakeHttp);
+ return cmd.getAll().then(resp => {
+ expect(fakeHttp.request).toHaveBeenCalledWith('get', {
+ args: {
+ cmd: 'get_reports',
+ details: 0,
+ filter: ALL_FILTER.toFilterString(),
+ usage_type: 'audit',
+ },
+ });
+ const {data} = resp;
+ expect(data.length).toEqual(2);
+ });
+ });
+
+ test('should return results', () => {
+ const response = createEntitiesResponse('report', [
+ {
+ _id: '1',
+ },
+ {
+ _id: '2',
+ },
+ ]);
+
+ const fakeHttp = createHttp(response);
+
+ expect.hasAssertions();
+
+ const cmd = new AuditReportsCommand(fakeHttp);
+ return cmd.get().then(resp => {
+ expect(fakeHttp.request).toHaveBeenCalledWith('get', {
+ args: {
+ cmd: 'get_reports',
+ details: 0,
+ usage_type: 'audit',
+ },
+ });
+ const {data} = resp;
+ expect(data.length).toEqual(2);
+ });
+ });
+
+ test('should aggregate compliance counts', () => {
+ const response = createAggregatesResponse();
+ const fakeHttp = createHttp(response);
+
+ expect.hasAssertions();
+
+ const cmd = new AuditReportsCommand(fakeHttp);
+ return cmd.getComplianceAggregates().then(resp => {
+ expect(fakeHttp.request).toHaveBeenCalledWith('get', {
+ args: {
+ cmd: 'get_aggregate',
+ aggregate_type: 'report',
+ group_column: 'compliant',
+ usage_type: 'audit',
+ },
+ });
+ });
+ });
+});
diff --git a/src/gmp/commands/__tests__/reports.js b/src/gmp/commands/__tests__/reports.js
index 07d7c03968..97cc6d922b 100644
--- a/src/gmp/commands/__tests__/reports.js
+++ b/src/gmp/commands/__tests__/reports.js
@@ -32,6 +32,7 @@ describe('ReportsCommand tests', () => {
cmd: 'get_reports',
details: 0,
filter: ALL_FILTER.toFilterString(),
+ usage_type: 'scan',
},
});
const {data} = resp;
@@ -59,6 +60,7 @@ describe('ReportsCommand tests', () => {
args: {
cmd: 'get_reports',
details: 0,
+ usage_type: 'scan',
},
});
const {data} = resp;
diff --git a/src/gmp/commands/auditreports.js b/src/gmp/commands/auditreports.js
new file mode 100644
index 0000000000..dcb6ccbebd
--- /dev/null
+++ b/src/gmp/commands/auditreports.js
@@ -0,0 +1,139 @@
+/* SPDX-FileCopyrightText: 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import {isDefined} from 'gmp/utils/identity';
+
+import registerCommand from 'gmp/command';
+
+import AuditReport from 'gmp/models/auditreport';
+
+import {ALL_FILTER} from 'gmp/models/filter';
+
+import DefaultTransform from 'gmp/http/transform/default';
+
+import {convertBoolean} from './convert';
+import EntitiesCommand from './entities';
+import EntityCommand from './entity';
+
+export class AuditReportsCommand extends EntitiesCommand {
+ constructor(http) {
+ super(http, 'report', AuditReport);
+ }
+
+ getEntitiesResponse(root) {
+ return root.get_reports.get_reports_response;
+ }
+
+ getComplianceAggregates({filter} = {}) {
+ return this.getAggregates({
+ aggregate_type: 'report',
+ group_column: 'compliant',
+ usage_type: 'audit',
+ filter,
+ });
+ }
+
+ get(params, options) {
+ return super.get(
+ {
+ details: 0,
+ ...params,
+ usage_type: 'audit',
+ },
+ options,
+ );
+ }
+}
+
+export class AuditReportCommand extends EntityCommand {
+ constructor(http) {
+ super(http, 'report', AuditReport);
+ }
+
+ download({id}, {reportFormatId, deltaReportId, filter}) {
+ return this.httpGet(
+ {
+ cmd: 'get_report',
+ delta_report_id: deltaReportId,
+ details: 1,
+ report_id: id,
+ report_format_id: reportFormatId,
+ filter: isDefined(filter) ? filter.all() : ALL_FILTER,
+ },
+ {transform: DefaultTransform, responseType: 'arraybuffer'},
+ );
+ }
+
+ addAssets({id}, {filter = ''}) {
+ return this.httpPost({
+ cmd: 'create_asset',
+ report_id: id,
+ filter,
+ });
+ }
+
+ removeAssets({id}, {filter = ''}) {
+ return this.httpPost({
+ cmd: 'delete_asset',
+ report_id: id,
+ filter,
+ });
+ }
+
+ alert({alert_id, report_id, filter}) {
+ return this.httpPost({
+ cmd: 'report_alert',
+ alert_id,
+ report_id,
+ filter,
+ });
+ }
+
+ getDelta(
+ {id},
+ {id: delta_report_id},
+ {filter, details = true, ...options} = {},
+ ) {
+ return this.httpGet(
+ {
+ id,
+ delta_report_id,
+ filter,
+ ignore_pagination: 1,
+ details: convertBoolean(details),
+ },
+ options,
+ ).then(this.transformResponse);
+ }
+
+ get(
+ {id},
+ {
+ filter,
+ details = true,
+ ignorePagination = true,
+ lean = true,
+ ...options
+ } = {},
+ ) {
+ return this.httpGet(
+ {
+ id,
+ filter,
+ lean: convertBoolean(lean),
+ ignore_pagination: convertBoolean(ignorePagination),
+ details: convertBoolean(details),
+ },
+ options,
+ ).then(this.transformResponse);
+ }
+
+ getElementFromRoot(root) {
+ return root.get_report.get_reports_response.report;
+ }
+}
+
+registerCommand('auditreport', AuditReportCommand);
+registerCommand('auditreports', AuditReportsCommand);
diff --git a/src/gmp/commands/reports.js b/src/gmp/commands/reports.js
index a1027e9160..b46fe58a23 100644
--- a/src/gmp/commands/reports.js
+++ b/src/gmp/commands/reports.js
@@ -52,6 +52,7 @@ export class ReportsCommand extends EntitiesCommand {
{
details: 0, // ensure to request no details by default
...params,
+ usage_type: 'scan',
},
options,
);
diff --git a/src/gmp/commands/users.js b/src/gmp/commands/users.js
index c94c4077e7..9a70662bcb 100644
--- a/src/gmp/commands/users.js
+++ b/src/gmp/commands/users.js
@@ -38,6 +38,7 @@ export const ROWS_PER_PAGE_SETTING_ID = '5f5a8712-8017-11e1-8556-406186ea4fc5';
export const DEFAULT_FILTER_SETTINGS = {
alert: 'b833a6f2-dcdc-4535-bfb0-a5154b5b5092',
asset: '0f040d06-abf9-43a2-8f94-9de178b0e978',
+ auditreport: '45414da7-55f0-44c1-abbb-6b7d1126fbdf',
certbund: 'e4cf514a-17e2-4ab9-9c90-336f15e24750',
cpe: '3414a107-ae46-4dea-872d-5c4479a48e8f',
credential: '186a5ac8-fe5a-4fb1-aa22-44031fb339f3',
@@ -286,6 +287,7 @@ export class UserCommand extends EntityCommand {
data.defaultTarget,
[saveDefaultFilterSettingId('alert')]: data.alertsFilter,
[saveDefaultFilterSettingId('asset')]: data.assetsFilter,
+ [saveDefaultFilterSettingId('auditreport')]: data.auditReportsFilter,
[saveDefaultFilterSettingId('scanconfig')]: data.configsFilter,
[saveDefaultFilterSettingId('credential')]: data.credentialsFilter,
[saveDefaultFilterSettingId('filter')]: data.filtersFilter,
diff --git a/src/gmp/gmp.js b/src/gmp/gmp.js
index fa52a2b89a..b233734851 100644
--- a/src/gmp/gmp.js
+++ b/src/gmp/gmp.js
@@ -11,6 +11,7 @@ import logger from 'gmp/log';
import 'gmp/commands/alerts';
import 'gmp/commands/audits';
+import 'gmp/commands/auditreports';
import 'gmp/commands/auth';
import 'gmp/commands/certbund';
import 'gmp/commands/credentials';
diff --git a/src/gmp/models/auditreport.js b/src/gmp/models/auditreport.js
new file mode 100644
index 0000000000..62438d2071
--- /dev/null
+++ b/src/gmp/models/auditreport.js
@@ -0,0 +1,65 @@
+/* SPDX-FileCopyrightText: 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import {_l} from 'gmp/locale/lang';
+
+import {isDefined} from 'gmp/utils/identity';
+
+import {parseDate} from 'gmp/parser';
+
+import Model, {parseModelFromElement} from 'gmp/model';
+
+import AuditReportReport from './report/auditreport';
+
+export const COMPLIANCE_STATES = {
+ yes: _l('Yes'),
+ no: _l('No'),
+ incomplete: _l('Incomplete'),
+ undefined: _l('Undefined'),
+};
+/* eslint-disable quote-props */
+
+export const getTranslatableReportCompliance = compliance =>
+ `${COMPLIANCE_STATES[compliance]}`;
+
+class AuditReport extends Model {
+ static entityType = 'auditreport';
+
+ static parseElement(element) {
+ const copy = super.parseElement(element);
+
+ const {
+ report,
+ report_format,
+ _type: type,
+ _content_type: content_type,
+ task,
+ scan_start,
+ scan_end,
+ timestamp,
+ } = element;
+
+ if (isDefined(report)) {
+ copy.report = AuditReportReport.fromElement(report);
+ }
+
+ copy.reportFormat = parseModelFromElement(report_format, 'reportformat');
+ copy.task = parseModelFromElement(task, 'task');
+
+ copy.reportType = type;
+ copy.contentType = content_type;
+
+ copy.scan_start = parseDate(scan_start);
+ copy.timestamp = parseDate(timestamp);
+
+ if (isDefined(scan_end)) {
+ copy.scan_end = parseDate(scan_end);
+ }
+
+ return copy;
+ }
+}
+
+export default AuditReport;
diff --git a/src/gmp/models/filter.js b/src/gmp/models/filter.js
index 5be78cc63f..09a3a6a9a3 100644
--- a/src/gmp/models/filter.js
+++ b/src/gmp/models/filter.js
@@ -768,6 +768,8 @@ class Filter extends Model {
export const ALL_FILTER = new Filter().all();
export const ALERTS_FILTER_FILTER = Filter.fromString('type=alert');
+export const AUDIT_REPORTS_FILTER_FILTER =
+ Filter.fromString('type=audit_report');
export const CERTBUND_FILTER_FILTER = Filter.fromString('type=info');
export const CPES_FILTER_FILTER = Filter.fromString('type=info');
export const CREDENTIALS_FILTER_FILTER = Filter.fromString('type=credential');
diff --git a/src/gmp/models/filter/keywords.js b/src/gmp/models/filter/keywords.js
index 739bc84973..f3468d7870 100644
--- a/src/gmp/models/filter/keywords.js
+++ b/src/gmp/models/filter/keywords.js
@@ -6,12 +6,14 @@
export const EXTRA_KEYWORDS = [
'apply_overrides',
+ 'compliance_levels',
'delta_states',
'first',
'levels',
'min_qod',
'notes',
'overrides',
+ 'report_compliance_levels',
'result_hosts_only',
'rows',
'solution_type',
diff --git a/src/gmp/models/report/auditreport.js b/src/gmp/models/report/auditreport.js
new file mode 100644
index 0000000000..18e69b4558
--- /dev/null
+++ b/src/gmp/models/report/auditreport.js
@@ -0,0 +1,140 @@
+/* SPDX-FileCopyrightText: 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import {isDefined} from 'gmp/utils/identity';
+import {isEmpty} from 'gmp/utils/string';
+
+import {parseDate} from 'gmp/parser';
+
+import {parseFilter} from 'gmp/collection/parser';
+
+import Model from 'gmp/model';
+
+import ReportTask from './task';
+
+import {
+ parse_errors,
+ parseHosts,
+ parseOperatingSystems,
+ parseResults,
+ parseTlsCertificates,
+} from './parser';
+
+class AuditReportReport extends Model {
+ static entityType = 'auditreport';
+
+ parseProperties(element) {
+ return AuditReportReport.parseElement(element);
+ }
+
+ static parseElement(element) {
+ const copy = super.parseElement(element);
+
+ const {
+ delta,
+ compliance,
+ compliance_count,
+ scan_start,
+ scan_end,
+ task,
+ scan,
+ timestamp,
+ } = element;
+
+ const filter = parseFilter(element);
+
+ copy.filter = filter;
+
+ copy.reportType = element._type;
+
+ delete copy.filters;
+
+ if (isDefined(compliance)) {
+ copy.compliance = {
+ filtered: compliance.filtered,
+ full: compliance.full,
+ };
+ }
+
+ if (isDefined(compliance_count)) {
+ copy.complianceCounts = {
+ filtered: parseInt(compliance_count.filtered),
+ full: parseInt(compliance_count.full),
+ incomplete: {
+ filtered: parseInt(compliance_count.incomplete.filtered),
+ full: parseInt(compliance_count.incomplete.full),
+ },
+ no: {
+ filtered: parseInt(compliance_count.no.filtered),
+ full: parseInt(compliance_count.no.full),
+ },
+ undefined: {
+ filtered: parseInt(compliance_count.undefined.filtered),
+ full: parseInt(compliance_count.undefined.full),
+ },
+ yes: {
+ filtered: parseInt(compliance_count.yes.filtered),
+ full: parseInt(compliance_count.yes.full),
+ },
+ };
+ }
+
+ delete copy.compliance_count;
+
+ copy.task = ReportTask.fromElement(task);
+
+ copy.results = parseResults(element, filter);
+
+ copy.hosts = parseHosts(element, filter);
+
+ copy.tlsCertificates = parseTlsCertificates(element, filter);
+
+ delete copy.host;
+
+ copy.operatingSystems = parseOperatingSystems(element, filter);
+
+ copy.errors = parse_errors(element, filter);
+
+ copy.scan_start = parseDate(scan_start);
+
+ if (isDefined(scan_end)) {
+ copy.scan_end = parseDate(scan_end);
+ }
+
+ if (isDefined(timestamp)) {
+ copy.timestamp = parseDate(timestamp);
+ }
+
+ if (isDefined(scan) && isDefined(scan.task) && isDefined(scan.task.slave)) {
+ if (isEmpty(scan.task.slave._id)) {
+ delete copy.scan.task.slave;
+ } else {
+ copy.slave = {
+ ...scan.task.slave,
+ };
+ }
+ }
+
+ if (isDefined(delta) && isDefined(delta.report)) {
+ copy.delta_report = {
+ id: delta.report._id,
+ scan_run_status: delta.report.scan_run_status,
+ scan_end: parseDate(delta.report.scan_end),
+ scan_start: parseDate(delta.report.scan_start),
+ timestamp: parseDate(delta.report.timestamp),
+ };
+
+ delete copy.delta;
+ }
+
+ return copy;
+ }
+
+ isDeltaReport() {
+ return this.reportType === 'delta';
+ }
+}
+
+export default AuditReportReport;
diff --git a/src/gmp/models/report/host.js b/src/gmp/models/report/host.js
index b22be1fe19..098e851886 100644
--- a/src/gmp/models/report/host.js
+++ b/src/gmp/models/report/host.js
@@ -38,6 +38,12 @@ class Host {
warning: 0,
total: 0,
};
+ this.complianceCounts = {
+ yes: 0,
+ no: 0,
+ incomplete: 0,
+ total: 0,
+ };
}
static fromElement(element) {
@@ -51,7 +57,19 @@ class Host {
static parseElement(element = {}) {
const copy = {...element};
- const {asset = {}, port_count = {}, result_count} = element;
+ const {
+ asset = {},
+ port_count = {},
+ result_count,
+ compliance_count,
+ host_compliance,
+ } = element;
+
+ copy.hostCompliance = isDefined(host_compliance)
+ ? host_compliance
+ : 'undefined';
+
+ delete copy.host_compliance;
if (isEmpty(asset._asset_id)) {
delete copy.asset;
@@ -82,10 +100,29 @@ class Host {
};
}
+ if (isDefined(compliance_count)) {
+ copy.complianceCounts = {
+ yes: parse_page_count(compliance_count.yes),
+ no: parse_page_count(compliance_count.no),
+ incomplete: parse_page_count(compliance_count.incomplete),
+ undefined: parse_page_count(compliance_count.undefined),
+ total: parse_page_count(compliance_count),
+ };
+ } else {
+ copy.complianceCounts = {
+ yes: 0,
+ no: 0,
+ incomplete: 0,
+ undefined: 0,
+ total: 0,
+ };
+ }
+
copy.start = parseDate(element.start);
copy.end = parseDate(element.end);
delete copy.result_count;
+ delete copy.compliance_count;
copy.authSuccess = {};
copy.details = {};
diff --git a/src/gmp/models/report/os.js b/src/gmp/models/report/os.js
index e90ed54b94..ebc6188e3d 100644
--- a/src/gmp/models/report/os.js
+++ b/src/gmp/models/report/os.js
@@ -11,6 +11,7 @@ class OperatingSystem {
constructor() {
this.hosts = {
hostsByIp: {},
+ complianceByIp: {},
count: 0,
};
}
@@ -22,6 +23,31 @@ class OperatingSystem {
}
}
+ addHostCompliance(host, compliance) {
+ if (!(host.ip in this.hosts.complianceByIp)) {
+ this.hosts.complianceByIp[host.ip] = compliance;
+ }
+ const complianceByIpValues = Object.values(this.hosts.complianceByIp);
+
+ const isNoInCompliance = complianceByIpValues.some(value => value === 'no');
+ const isIncompleteInCompliance = complianceByIpValues.some(
+ value => value === 'incomplete',
+ );
+ const isYesInCompliance = complianceByIpValues.some(
+ value => value === 'yes',
+ );
+
+ if (isNoInCompliance) {
+ this.compliance = 'no';
+ } else if (isIncompleteInCompliance) {
+ this.compliance = 'incomplete';
+ } else if (isYesInCompliance) {
+ this.compliance = 'yes';
+ } else {
+ this.compliance = 'undefined';
+ }
+ }
+
setSeverity(severity) {
if (!isDefined(this.severity) || this.severity < severity) {
this.severity = severity;
diff --git a/src/gmp/models/report/parser.js b/src/gmp/models/report/parser.js
index 65cec48744..da86443613 100644
--- a/src/gmp/models/report/parser.js
+++ b/src/gmp/models/report/parser.js
@@ -306,7 +306,7 @@ export const parseOperatingSystems = (report, filter) => {
const severities = parseHostSeverities(results);
forEach(hosts, host => {
- const {detail: details, ip} = host;
+ const {detail: details, ip, host_compliance} = host;
let best_os_cpe;
let best_os_txt;
@@ -335,6 +335,7 @@ export const parseOperatingSystems = (report, filter) => {
os.addHost(host);
os.setSeverity(severity);
+ os.addHostCompliance(host, host_compliance);
}
}
});
@@ -398,7 +399,7 @@ export const parseHosts = (report, filter) => {
const parse_report_report_counts = elem => {
const es = isDefined(elem.results) ? elem.results : {};
- const ec = elem.result_count;
+ const ec = elem.result_count ? elem.result_count : elem.compliance_count;
const length = isDefined(es.result) ? es.result.length : 0;
@@ -413,9 +414,13 @@ const parse_report_report_counts = elem => {
};
export const parseResults = (report, filter) => {
- const {results, result_count} = report;
+ const {results, result_count, compliance_count} = report;
- if (!isDefined(results) && !isDefined(result_count)) {
+ if (
+ !isDefined(results) &&
+ !isDefined(result_count) &&
+ !isDefined(compliance_count)
+ ) {
return undefined;
// instead of returning empty_collection_list(filter) we return an undefined
// in order to query if results have been loaded and make a difference to
diff --git a/src/gmp/models/result.js b/src/gmp/models/result.js
index a6291aa5f2..73cdf7901c 100644
--- a/src/gmp/models/result.js
+++ b/src/gmp/models/result.js
@@ -42,6 +42,7 @@ class Result extends Model {
const copy = super.parseElement(element);
const {
+ compliance,
description,
detection,
host = {},
@@ -88,6 +89,10 @@ class Result extends Model {
copy.description = description;
}
+ if (isDefined(compliance)) {
+ copy.compliance = compliance;
+ }
+
if (isDefined(severity)) {
copy.severity = parseSeverity(severity);
}
diff --git a/src/gmp/utils/entitytype.js b/src/gmp/utils/entitytype.js
index 6938a59eb1..815cd00d42 100644
--- a/src/gmp/utils/entitytype.js
+++ b/src/gmp/utils/entitytype.js
@@ -34,6 +34,7 @@ export const pluralizeType = type => {
return type + 's';
};
const TYPES = {
+ audit_report: 'auditreport',
config: 'scanconfig',
cert_bund_adv: 'certbund',
dfn_cert_adv: 'dfncert',
@@ -61,6 +62,8 @@ export const normalizeType = type => {
const ENTITY_TYPES = {
alert: _l('Alert'),
asset: _l('Asset'),
+ audit: _l('Audit'),
+ auditreport: _l('Audit Report'),
certbund: _l('CERT-Bund Advisory'),
cpe: _l('CPE'),
credential: _l('Credential'),
@@ -75,6 +78,7 @@ const ENTITY_TYPES = {
note: _l('Note'),
nvt: _l('NVT'),
permission: _l('Permission'),
+ policy: _l('Policy'),
portlist: _l('Port List'),
portrange: _l('Port Range'),
report: _l('Report'),
@@ -108,6 +112,7 @@ export const typeName = type => {
};
const CMD_TYPES = {
+ auditreport: 'audit_report',
scanconfig: 'config',
certbund: 'cert_bund_adv',
dfncert: 'dfn_cert_adv',
diff --git a/src/web/components/bar/compliancebar.jsx b/src/web/components/bar/compliancebar.jsx
new file mode 100644
index 0000000000..9f17e89fc9
--- /dev/null
+++ b/src/web/components/bar/compliancebar.jsx
@@ -0,0 +1,44 @@
+/* SPDX-FileCopyrightText: 2024 Greenbone AG
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import React from 'react';
+
+import {isDefined} from 'gmp/utils/identity';
+
+import PropTypes from 'web/utils/proptypes';
+import Theme from 'web/utils/theme';
+
+import ProgressBar from './progressbar';
+import {getTranslatableReportCompliance} from 'gmp/models/auditreport';
+
+const ComplianceBar = ({compliance, toolTip}) => {
+ const title = getTranslatableReportCompliance(compliance);
+
+ let background;
+ if (compliance === 'no') {
+ background = Theme.complianceNo;
+ } else if (compliance === 'incomplete') {
+ background = Theme.complianceIncomplete;
+ } else if (compliance === 'yes') {
+ background = Theme.complianceYes;
+ } else {
+ background = Theme.complianceUndefined;
+ }
+
+ const toolTipText = isDefined(toolTip) ? toolTip : title;
+
+ return (
+
+ {title}
+
+ );
+};
+
+ComplianceBar.propTypes = {
+ compliance: PropTypes.string,
+ toolTip: PropTypes.string,
+};
+
+export default ComplianceBar;
\ No newline at end of file
diff --git a/src/web/components/bar/compliancestatusbar.jsx b/src/web/components/bar/compliancestatusbar.jsx
index a3fbe98afe..16917b57f2 100644
--- a/src/web/components/bar/compliancestatusbar.jsx
+++ b/src/web/components/bar/compliancestatusbar.jsx
@@ -5,7 +5,7 @@
import React from 'react';
-import _ from 'gmp/locale';
+import useTranslation from 'web/hooks/useTranslation';
import PropTypes from 'web/utils/proptypes';
import Theme from 'web/utils/theme';
@@ -15,6 +15,7 @@ import ProgressBar from 'web/components/bar/progressbar';
const ComplianceStatusBar = ({complianceStatus}) => {
let text;
let boxBackground;
+ const [_] = useTranslation();
if (complianceStatus < 0 || complianceStatus > 100) {
text = _('N/A');
boxBackground = Theme.darkGrey;
diff --git a/src/web/components/bar/menubar.jsx b/src/web/components/bar/menubar.jsx
index 8d5e567a03..9d1315365b 100644
--- a/src/web/components/bar/menubar.jsx
+++ b/src/web/components/bar/menubar.jsx
@@ -25,8 +25,7 @@ import {isLoggedIn} from 'web/store/usersettings/selectors';
import compose from 'web/utils/compose';
import PropTypes from 'web/utils/proptypes';
import Theme from 'web/utils/theme';
-import withGmp from 'web/utils/withGmp';
-import withCapabilities from 'web/utils/withCapabilities';
+import useCapabilities from 'web/hooks/useCapabilities';
const MENU_BAR_HEIGHT = '35px';
@@ -54,8 +53,11 @@ const MenuBarPlaceholder = styled.div`
`;
// eslint-disable-next-line no-shadow
-const MenuBar = ({isLoggedIn, capabilities}) => {
- if (!isLoggedIn || !isDefined(capabilities)) {
+const MenuBar = ({isLoggedIn}) => {
+
+ const caps = useCapabilities();
+
+ if (!isLoggedIn || !isDefined(caps)) {
return null;
}
@@ -66,7 +68,7 @@ const MenuBar = ({isLoggedIn, capabilities}) => {
'vulns',
'overrides',
'notes',
- ].reduce((sum, cur) => sum || capabilities.mayAccess(cur), false);
+ ].reduce((sum, cur) => sum || caps.mayAccess(cur), false);
const may_op_configuration = [
'targets',
@@ -80,10 +82,10 @@ const MenuBar = ({isLoggedIn, capabilities}) => {
'scanners',
'filters',
'tags',
- ].reduce((sum, cur) => sum || capabilities.mayAccess(cur), false);
+ ].reduce((sum, cur) => sum || caps.mayAccess(cur), false);
const mayOpNotesOverrides = ['notes', 'overrides'].reduce(
- (sum, cur) => sum || capabilities.mayAccess(cur),
+ (sum, cur) => sum || caps.mayAccess(cur),
false,
);
@@ -91,20 +93,20 @@ const MenuBar = ({isLoggedIn, capabilities}) => {
'alerts',
'schedules',
'report_formats',
- ].reduce((sum, cur) => sum || capabilities.mayAccess(cur), false);
+ ].reduce((sum, cur) => sum || caps.mayAccess(cur), false);
const mayOpScannersFiltersTags = ['scanners', 'filters', 'tags'].reduce(
- (sum, cur) => sum || capabilities.mayAccess(cur),
+ (sum, cur) => sum || caps.mayAccess(cur),
false,
);
const mayOpResilience = ['tickets', 'policies', 'audits'].reduce(
- (sum, cur) => sum || capabilities.mayAccess(cur),
+ (sum, cur) => sum || caps.mayAccess(cur),
false,
);
const mayOpAssets = ['assets', 'tls_certificates'].reduce(
- (sum, cur) => sum || capabilities.mayAccess(cur),
+ (sum, cur) => sum || caps.mayAccess(cur),
false,
);
@@ -116,24 +118,24 @@ const MenuBar = ({isLoggedIn, capabilities}) => {
{may_op_scans && (