Published on
 // 29 min read

All About ARF

Authors

I think that the Asset Reporting Format (ARF) is one of the best-kept secrets about the OpenSCAP Scanner. With just a simple switch you can turn a frustratingly plain compliance report into something actionable, with information you can use to modify system configuration and return it to a compliant baseline!

The inspiration for this article is a thread with Ben Blasco on all things ARF, which included this wonderful pic:

comic

Before we dive in, I need to get a super quick compliance-related Dad joke out the way.

What sound does a SCAPendoes make?

ARF!

With that out the way - let's explore ARF!

What is the Asset Reporting Format (ARF)?

The Asset Reporting Format (ARF) is an XML-based standard developed as part of the NIST Security Content Automation Protocol (SCAP) suite, designed to make it easier to report on the state of IT assets in a structured, machine-readable way.

ARF provides an important role in organisations. One of the common challenges organisations is understanding "what do I actually own / have, and how is it configured?" ARF supports this by leveraging asset identification for ARF reports. Each asset in an ARF report is uniquely identified, and this provides a way to tie data about assets from different sources. In fact, the 'asset' section of ARF reports contains information just about the asset - independent of the relationship to reports.

You can see an example of an ARF report here:

<?xml version="1.0" encoding="UTF-8"?>
<asset-report-collection xmlns:ai="http://scap.nist.gov/schema/asset-identification/1.1"
    xmlns="http://scap.nist.gov/schema/asset-reporting-format/1.1"
    xmlns:core="http://scap.nist.gov/schema/reporting-core/1.1"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://scap.nist.gov/schema/asset-reporting-format/1.1 http://scap.nist.gov/schema/asset-reporting-format/1.1/asset-reporting-format_1.1.0.xsd">
    <core:relationships xmlns:arfvocab="http://scap.nist.gov/vocabulary/arf/relationships/1.0#">
        <core:relationship type="arfvocab:isAbout" subject="report_1">
            <core:ref>asset_1</core:ref>
        </core:relationship>
        <core:relationship type="arfvocab:createdFor" subject="report_1">
            <core:ref>report_request_1</core:ref>
        </core:relationship>
    </core:relationships>
    <report-requests>
        <report-request id="report_request_1">
            <content>
                <Benchmark id="minimal-xccdf" xml:lang="en-US"
                    xmlns="http://checklists.nist.gov/xccdf/1.1"
                    xmlns:cpe="http://cpe.mitre.org/dictionary/2.0"
                    xmlns:dc="http://purl.org/dc/elements/1.1/"
                    xmlns:xhtml="http://www.w3.org/1999/xhtml">
                    <status date="2009-12-01">draft</status>
                    <title>Test Title</title>
                    <description>
                        <xhtml:strong>Test Description</xhtml:strong>
                    </description>
                    <notice id="test-notice">Test Notice</notice>
                    <reference href="http://testreference1">
                        <dc:publisher>Test Publisher1</dc:publisher>
                        <dc:identifier>Test Identifier1</dc:identifier>
                    </reference>
                    <platform idref="cpe:/o:microsoft:windows_vista"/>
                    <version>Test Version</version>
                    <metadata>
                        <dc:creator>Test Creator</dc:creator>
                        <dc:publisher>Test Publisher</dc:publisher>
                        <dc:contributor>Test Contributor</dc:contributor>
                        <dc:source>http://scap.nist.gov/</dc:source>
                    </metadata>
                    <Profile id="test_profile1">
                        <title>Test Title for Profile 1</title>
                        <description>Test Description for Profile 1</description>
                        <select idref="test_rule1" selected="true"/>
                    </Profile>
                    <Rule id="test_rule1" selected="true" weight="10.0">
                        <title>Test Title for Rule 1</title>
                        <description>Test Description for Rule 1</description>
                        <ident system="http://cce.mitre.org">CCE-2466-1</ident>
                        <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
                            <check-content-ref href="minimal-oval.xml" name="oval:gov.nist.test.compliance:def:1"/>
                        </check>
                    </Rule>
                </Benchmark>
            </content>
        </report-request>
    </report-requests>
    <assets>
        <asset id="asset_1">
            <ai:computing-device>
                <ai:connections>
                    <ai:connection>
                        <ai:ip-address>
                            <ai:ip-v4>192.168.2.10</ai:ip-v4>
                        </ai:ip-address>
                    </ai:connection>
                </ai:connections>
                <ai:fqdn>comp1234.tempuri.org</ai:fqdn>
            </ai:computing-device>
        </asset>
    </assets>
    <reports>
        <report id="report_1">
            <content>
                <TestResult xmlns="http://checklists.nist.gov/xccdf/1.1" id="minimal-xccdf1280857747215" version="Test Version" test-system="cpe:/a:nist:scap_scanner:1.0"
start-time="2010-08-03T13:44:07.657-04:00" end-time="2010-08-03T13:49:07.657-04:00">
                    <benchmark href="minimal-xccdf"/>
                    <title xml:lang="en-US">SCAP automated assessment for checklist minimal-xccdf performed at Tuesday, August 3, 2010</title>
                    <organization>National Institute of Standards and Technology</organization>
                    <identity authenticated="1" privileged="1">administrator</identity>
                    <profile idref="test_profile1"/>
                    <target>0:0:0:0:0:0:0:1</target>
                    <target>127.0.0.1</target>
                    <target>host.domain.tld</target>
                    <target-address>0:0:0:0:0:0:0:1</target-address>
                    <target-address>127.0.0.1</target-address>
                    <target-address>192.168.222.1</target-address>
                    <target-facts>
                        <fact name="urn:xccdf:fact:asset:identifier:host_name" type="string">0:0:0:0:0:0:0:1</fact>
                        <fact name="urn:xccdf:fact:asset:identifier:fqdn" type="string">0:0:0:0:0:0:0:1</fact>
                        <fact name="urn:xccdf:fact:asset:identifier:ipv6" type="string">0:0:0:0:0:0:0:1</fact>
                        <fact name="urn:xccdf:fact:asset:identifier:host_name" type="string">127.0.0.1</fact>
                        <fact name="urn:xccdf:fact:asset:identifier:fqdn" type="string">127.0.0.1</fact>
                        <fact name="urn:xccdf:fact:asset:identifier:ipv4" type="string">127.0.0.1</fact>
                        <fact name="urn:xccdf:fact:asset:identifier:mac" type="string"/>
                        <fact name="urn:xccdf:fact:asset:identifier:host_name" type="string">host.domain.tld</fact>
                        <fact name="urn:xccdf:fact:asset:identifier:fqdn" type="string">host.domain.tld</fact>
                        <fact name="urn:xccdf:fact:asset:identifier:ipv4" type="string">192.168.222.1</fact>
                        <fact name="urn:xccdf:fact:asset:identifier:mac" type="string">00:50:56:c0:00:01</fact>
                    </target-facts>
                    <rule-result idref="test_rule1" time="2010-08-03T13:49:07.650-04:00" weight="10.0">
                        <result>pass</result>
                        <ident system="http://cce.mitre.org">CCE-2466-1</ident>
                        <instance>host.domain.tld</instance>
                        <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
                            <check-content-ref href="minimal-oval-res.xml" name="oval:gov.nist.test.compliance:def:1"/>
                        </check>
                    </rule-result>
                    <score maximum="1" system="urn:xccdf:scoring:flat-unweighted">1</score>
                    <score maximum="10" system="urn:xccdf:scoring:flat">10</score>
                </TestResult>
            </content>
        </report>
    </reports>
</asset-report-collection>

Let's break down this example. The asset-report-collection is the top-level container for an ARF report, and contains assets, reports and relationships.

<asset-report-collection xmlns:ai="http://scap.nist.gov/schema/asset-identification/1.1"
    xmlns="http://scap.nist.gov/schema/asset-reporting-format/1.1"
    xmlns:core="http://scap.nist.gov/schema/reporting-core/1.1"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://scap.nist.gov/schema/asset-reporting-format/1.1
    http://scap.nist.gov/schema/asset-reporting-format/1.1/asset-reporting-format_1.1.0.xsd">
...
<asset-report-collection>

ARF primarily exists to express information about assets and the relationships between assets and reports. You can see this in the core:relationships section. This highlights that the ARF document contains a report report_1, which is about an asset, asset_1.

<core:relationships xmlns:arfvocab="http://scap.nist.gov/vocabulary/arf/relationships/1.0#">
    <core:relationship type="arfvocab:isAbout" subject="report_1">
        <core:ref>asset_1</core:ref>
    </core:relationship>
    <core:relationship type="arfvocab:createdFor" subject="report_1">
        <core:ref>report_request_1</core:ref>
    </core:relationship>
</core:relationships>

ARF documents need to contain a section report-requests. This basically describes the report created, who / what it was created for, and some information about the report. In our example, an XCCDF report (a compliance scan) has been requested or a Microsoft Windows Vista system, with several explicit compliance rules defined.

<report-requests>
    <report-request id="report_request_1">
        <content>
            <Benchmark id="minimal-xccdf" xml:lang="en-US"
                xmlns="http://checklists.nist.gov/xccdf/1.1"
                xmlns:cpe="http://cpe.mitre.org/dictionary/2.0"
                xmlns:dc="http://purl.org/dc/elements/1.1/"
                xmlns:xhtml="http://www.w3.org/1999/xhtml">
                <status date="2009-12-01">draft</status>
                <title>Test Title</title>
                <description>
                    <xhtml:strong>Test Description</xhtml:strong>
                </description>
                <notice id="test-notice">Test Notice</notice>
                <reference href="http://testreference1">
                    <dc:publisher>Test Publisher1</dc:publisher>
                    <dc:identifier>Test Identifier1</dc:identifier>
                </reference>
                <platform idref="cpe:/o:microsoft:windows_vista"/>
                <version>Test Version</version>
                <metadata>
                    <dc:creator>Test Creator</dc:creator>
                    <dc:publisher>Test Publisher</dc:publisher>
                    <dc:contributor>Test Contributor</dc:contributor>
                    <dc:source>http://scap.nist.gov/</dc:source>
                </metadata>
                <Profile id="test_profile1">
                    <title>Test Title for Profile 1</title>
                    <description>Test Description for Profile 1</description>
                    <select idref="test_rule1" selected="true"/>
                </Profile>
                <Rule id="test_rule1" selected="true" weight="10.0">
                    <title>Test Title for Rule 1</title>
                    <description>Test Description for Rule 1</description>
                    <ident system="http://cce.mitre.org">CCE-2466-1</ident>
                    <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
                        <check-content-ref href="minimal-oval.xml" name="oval:gov.nist.test.compliance:def:1"/>
                    </check>
                </Rule>
            </Benchmark>
        </content>
    </report-request>
</report-requests>

Ok, so far we know that:

  • ARF reports describe relationships between assets and reports
  • Assets are uniquely identified in ARF reports

In our example, the ARF document describes an XCCDF scan (the report), which was conducted for a Microsoft Windows Vista system (the asset). There's more detail about the asset in the assets section:

<assets>
    <asset id="asset_1">
        <ai:computing-device>
            <ai:connections>
                <ai:connection>
                    <ai:ip-address>
                        <ai:ip-v4>192.168.2.10</ai:ip-v4>
                    </ai:ip-address>
                </ai:connection>
            </ai:connections>
            <ai:fqdn>comp1234.tempuri.org</ai:fqdn>
        </ai:computing-device>
    </asset>
</assets>

Great! Now we know that this XCCDF scan was requested for a Microsoft Windows Vista system with the IPv4 address 192.168.2.10 and hostname comp1234.tempuri.org.

Finally, the XCCDF scan results are shown in the reports section. This section can contain many reports, but we only have one - an XCCDF scan for this system.

<report id="report_1">
    <content>
        <TestResult xmlns="http://checklists.nist.gov/xccdf/1.1" id="minimal-xccdf1280857747215" version="Test Version" test-system="cpe:/a:nist:scap_scanner:1.0"
start-time="2010-08-03T13:44:07.657-04:00" end-time="2010-08-03T13:49:07.657-04:00">
            <benchmark href="minimal-xccdf"/>
            <title xml:lang="en-US">SCAP automated assessment for checklist minimal-xccdf performed at Tuesday, August 3, 2010</title>
            <organization>National Institute of Standards and Technology</organization>
            <identity authenticated="1" privileged="1">administrator</identity>
            <profile idref="test_profile1"/>
            <target>0:0:0:0:0:0:0:1</target>
            <target>127.0.0.1</target>
            <target>host.domain.tld</target>
            <target-address>0:0:0:0:0:0:0:1</target-address>
            <target-address>127.0.0.1</target-address>
            <target-address>192.168.222.1</target-address>
            <target-facts>
                <fact name="urn:xccdf:fact:asset:identifier:host_name" type="string">0:0:0:0:0:0:0:1</fact>
                <fact name="urn:xccdf:fact:asset:identifier:fqdn" type="string">0:0:0:0:0:0:0:1</fact>
                <fact name="urn:xccdf:fact:asset:identifier:ipv6" type="string">0:0:0:0:0:0:0:1</fact>
                <fact name="urn:xccdf:fact:asset:identifier:host_name" type="string">127.0.0.1</fact>
                <fact name="urn:xccdf:fact:asset:identifier:fqdn" type="string">127.0.0.1</fact>
                <fact name="urn:xccdf:fact:asset:identifier:ipv4" type="string">127.0.0.1</fact>
                <fact name="urn:xccdf:fact:asset:identifier:mac" type="string"/>
                <fact name="urn:xccdf:fact:asset:identifier:host_name" type="string">host.domain.tld</fact>
                <fact name="urn:xccdf:fact:asset:identifier:fqdn" type="string">host.domain.tld</fact>
                <fact name="urn:xccdf:fact:asset:identifier:ipv4" type="string">192.168.222.1</fact>
                <fact name="urn:xccdf:fact:asset:identifier:mac" type="string">00:50:56:c0:00:01</fact>
            </target-facts>
            <rule-result idref="test_rule1" time="2010-08-03T13:49:07.650-04:00" weight="10.0">
                <result>pass</result>
                <ident system="http://cce.mitre.org">CCE-2466-1</ident>
                <instance>host.domain.tld</instance>
                <check system="http://oval.mitre.org/XMLSchema/oval-definitions-5">
                    <check-content-ref href="minimal-oval-res.xml" name="oval:gov.nist.test.compliance:def:1"/>
                </check>
            </rule-result>
            <score maximum="1" system="urn:xccdf:scoring:flat-unweighted">1</score>
            <score maximum="10" system="urn:xccdf:scoring:flat">10</score>
        </TestResult>
    </content>
</report>

Scanning systems with XCCDF and OpenSCAP

Now that we know about the ARF format let's see it in action. Firstly, I want to run a scan without ARF, and see what the results look like. Let's run an Australian ISM benchmark scan of a Red Hat Enteprise Linux 9 system using the 'official' level controls and collect the results in XCCDF format (not ARF).

I want to show how ARF results can look for misconfigured systems, so let's place a world-writeable file in /etc:

touch /etc/my-wr-file
chmod 777 /etc/my-wr-file

Let's find a profile to use for the scan. You can check the available XCCDF profiles available on a RHEL system with the oscap info command:

$ oscap info /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
...
Document type: Source Data Stream
Imported: 2025-03-10T19:32:05

Stream: scap_org.open-scap_datastream_from_xccdf_ssg-rhel9-xccdf.xml
Generated: (null)
Version: 1.3
Checklists:
	Ref-Id: scap_org.open-scap_cref_ssg-rhel9-xccdf.xml
		Status: draft
		Generated: 2025-02-25
		Resolved: true
		Profiles:
			Title: ANSSI-BP-028 (enhanced)
				Id: xccdf_org.ssgproject.content_profile_anssi_bp28_enhanced
            ... .... ...
            ... snip ...
            ... .... ...
			Title: Australian Cyber Security Centre (ACSC) ISM Official
				Id: xccdf_org.ssgproject.content_profile_ism_o

Let's run a scan using the Australian ISM profile:

$ oscap xccdf eval --results results.xml --profile xccdf_org.ssgproject.content_profile_ism_o /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml

Once this completes, you can see that the results file is XCCDF, not ARF:

'results.xml'
<?xml version="1.0" encoding="UTF-8"?>
<Benchmark xmlns="http://checklists.nist.gov/xccdf/1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="xccdf_org.ssgproject.content_benchmark_RHEL-9" resolved="1" xml:lang="en-US" style="SCAP_1.2">
  <status date="2025-02-25">draft</status>
  <title xmlns:xhtml="http://www.w3.org/1999/xhtml" xml:lang="en-US">Guide to the Secure Configuration of Red Hat Enterprise Linux 9</title>
...

Let's create a HTML file and serve it out with python, to make the results easier to read:

$ oscap xccdf generate report results.xml > results.html
$ python -m http.server

The report intro describes the SCAP profile used and scan target:

xccdf1

Further down I can find the control that failed, because we created a world-writeable file:

xccdf2

If I click this control, I can bring up more detail:

xccdf3
xccdf4

We can see that this system failed the 'world-writeable files' control. But, there's no information on which file failed the control. This makes it pretty difficult to remediate this system.

How do we get actionable data?

ARF to the rescue

We can use the --results-arf switch with the OpenSCAP scanner to emit results in ARF. Let's modify the scan we used previously:

$ oscap xccdf eval --results-arf results-arf.xml --profile xccdf_org.ssgproject.content_profile_ism_o /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml

If we quickly check the file contents we can see that it contains the now-familiar asset-report-collection tags:

'results-arf.xml'
<?xml version="1.0" encoding="UTF-8"?>
<arf:asset-report-collection xmlns:arf="http://scap.nist.gov/schema/asset-reporting-format/1.1" xmlns:core="http://scap.nist.gov/schema/reporting-core/1.1" xmlns:ai="http://scap.nist.gov/schema/asset-identification/1.1">

The relationships identify that this is an XCCDF scan for a system:

'results-arf.xml'
<core:relationships xmlns:arfvocab="http://scap.nist.gov/specifications/arf/vocabulary/relationships/1.0#">
    <core:relationship type="arfvocab:createdFor" subject="xccdf1">
        <core:ref>collection1</core:ref>
    </core:relationship>
    <core:relationship type="arfvocab:isAbout" subject="xccdf1">
        <core:ref>asset0</core:ref>
    </core:relationship>
</core:relationships>

The assets section identifies the system evaluated via XCCDF controls:

'results-arf.xml'
<arf:assets>
    <arf:asset id="asset0">
        <ai:computing-device>
        <ai:connections>
            <ai:connection>
            <ai:ip-address>
                <ai:ip-v4>127.0.0.1</ai:ip-v4>
            </ai:ip-address>
            </ai:connection>
            <ai:connection>
            <ai:ip-address>
                <ai:ip-v4>192.168.122.237</ai:ip-v4>
            </ai:ip-address>
            </ai:connection>
            <ai:connection>
            <ai:ip-address>
                <ai:ip-v6>0:0:0:0:0:0:0:1</ai:ip-v6>
            </ai:ip-address>
            </ai:connection>
            <ai:connection>
            <ai:ip-address>
                <ai:ip-v6>fe80:0:0:0:5054:ff:fe97:7f07</ai:ip-v6>
            </ai:ip-address>
            </ai:connection>
            <ai:connection>
            <ai:mac-address>00:00:00:00:00:00</ai:mac-address>
            </ai:connection>
            <ai:connection>
            <ai:mac-address>52:54:00:97:7F:07</ai:mac-address>
            </ai:connection>
        </ai:connections>
        <ai:fqdn>rhel-arf.rock.lab</ai:fqdn>
        <ai:hostname>rhel-arf.rock.lab</ai:hostname>
        </ai:computing-device>
    </arf:asset>
</arf:assets>

And finally, the reports section identifies the XCCDF scan content:

'results-arf.xml'
<arf:reports>
    <arf:report id="xccdf1">
      <arf:content>
        <TestResult xmlns="http://checklists.nist.gov/xccdf/1.2" id="xccdf_org.open-scap_testresult_xccdf_org.ssgproject.content_profile_ism_o" start-time="2025-04-25T21:27:45+09:30" end-time="2025-04-25T21:29:09+09:30" version="0.1.76" test-system="cpe:/a:redhat:openscap:1.3.10">
          <benchmark href="#scap_org.open-scap_comp_ssg-rhel9-xccdf.xml" id="xccdf_org.ssgproject.content_benchmark_RHEL-9"/>
          <title>OSCAP Scan Result</title>
          <identity authenticated="false" privileged="false">user1</identity>
          <profile idref="xccdf_org.ssgproject.content_profile_ism_o"/>
          <target>rhel-arf.rock.lab</target>
          <target-address>127.0.0.1</target-address>
        ... .... ...
        ... snip ...
        ... .... ...
        </TestResult>
      </arf:content>
    </arf:report>
</arf:reports>

While I'd love to continue breaking this XML down line-by-line, I think we need something more readable:

$ oscap xccdf generate report results-arf.xml > results-arf.html

The report looks pretty standard:

arf1

But, if I check the results, it now lists the files that have failed the XCCDF control.

arf2

This is great! Using ARF I can see why my security controls are failing tests, and remediate.

ARF and OpenShift

OpenShift includes the Compliance Operator, and you can use this to emit results in ARF. In fact, the Compliance Operator will always generate results in ARF, and you can verify this in the code.

The Compliance Operator creates a temporary file to hold the ARF report in /tmp/report-arf.xml, It then appends --results-arf to the OpenSCAP scanner command - similar to how we defined this above - before running the scan:

var defaultOpenScapScriptContents = `#!/bin/bash

ARF_REPORT=/tmp/report-arf.xml

if [ -z $PROFILE ]; then
    echo "profile not set"
    exit 1
fi

... .... ...
... snip ...
... .... ...

cmd+=(
    --profile $PROFILE \
    --results-arf $ARF_REPORT
)

if [ ! -z $RULE ]; then
    cmd+=(--rule $RULE)
fi

cmd+=($CONTENT)
exit 0`

Let's see this in action too. I'm going to use Red Hat Advanced Cluster Security for Kubernetes (RHACS) to schedule a compliance scan against this OpenShift cluster. If you want to configure this yourself, you can see my setup in the article Who is watching the watchers?

Firstly, we need to ensure that the Compliance Operator is deployed to the target cluster and that the cluster is enrolled with RHACS Central:

co1

Now create a scan schedule via RHACS:

scan1
scan2
scan3
scan4
scan5

I can see that the scan is kicking off in OpenShift and results are being generated:

scan6

Once the scan completes, I can retrieve the raw ARF by creating a new pod and mounting the ocp4-e8 PV that was created for the scan. Firstly, let's check which persistent volume claim is being used to hold compliance results storage:

$ oc get compliancesuites -n openshift-compliance
NAME          PHASE   RESULT
ocp-e8-scan   DONE    NON-COMPLIANT

$ oc get compliancesuites ocp-e8-scan -n openshift-compliance -o json | jq '.status.scanStatuses[].result
sStorage'
{
  "name": "ocp4-e8",
  "namespace": "openshift-compliance"
}

Now that we know the PVC, let's create a pod to mount the PVC, and allow us to extract the raw ARF:

$ oc create -n openshift-compliance -f - <<EOF
apiVersion: "v1"
kind: Pod
metadata:
  name: pv-extract
spec:
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: pv-extract-pod
      image: registry.access.redhat.com/ubi9/ubi
      command: ["sleep", "3000"]
      volumeMounts:
      - mountPath: "/workers-scan-results"
        name: workers-scan-vol
      securityContext:
        allowPrivilegeEscalation: false
        capabilities:
          drop: [ALL]
  volumes:
    - name: workers-scan-vol
      persistentVolumeClaim:
        claimName: ocp4-e8
EOF

Check that the pod was created:

$ oc get pods -n openshift-compliance | grep pv-extract
pv-extract                                        1/1     Running     0             39s

Now we can copy out the ARF results doc:

$ oc cp pv-extract:/workers-scan-results -n openshift-compliance .

What you'll get from the copy operation out of the pod is a list of directories, each containing the raw scan results in a bzip2-compressed file:

$ tree
.
├── 2
│   └── ocp4-e8-api-checks-pod.xml.bzip2
├── 3
│   └── ocp4-e8-api-checks-pod.xml.bzip2
├── 4
│   └── ocp4-e8-api-checks-pod.xml.bzip2
└── lost+found

5 directories, 3 files

Let's take a look at the scan in directory 2:

$ cd 2
$ bzip2 -d ocp4-e8-api-checks-pod.xml.bzip2
bzip2: Can't guess original name for ocp4-e8-api-checks-pod.xml.bzip2 -- using ocp4-e8-api-checks-pod.xml.bzip2.out
$ mv ocp4-e8-api-checks-pod.xml.bzip2.out ocp4-e8-api-checks-pod.xml

Let's check out this new file, ocp4-e8-api-checks-pod.xml:

$ head ocp4-e8-api-checks-pod.xml

<?xml version="1.0" encoding="UTF-8"?>
<arf:asset-report-collection xmlns:arf="http://scap.nist.gov/schema/asset-reporting-format/1.1" xmlns:core="http://scap.nist.gov/schema/reporting-core/1.1" xmlns:ai="http://scap.nist.gov/schema/asset-identification/1.1">
  <core:relationships xmlns:arfvocab="http://scap.nist.gov/specifications/arf/vocabulary/relationships/1.0#">
    <core:relationship type="arfvocab:createdFor" subject="xccdf1">
      <core:ref>collection1</core:ref>
    </core:relationship>
    <core:relationship type="arfvocab:isAbout" subject="xccdf1">
      <core:ref>asset0</core:ref>
    </core:relationship>
  </core:relationships>
...

Great! We've got our familiar asset-report-collection root element, signifying that this is an ARF document. Let's create a HTML report to make this easier to read:

$ oscap xccdf generate report ocp4-e8-api-checks-pod.xml > ocp4-e8-api-checks-pod.xml.html

The report looks pretty good so far:

scan7

Let's check out one of the failing rules, validating that OpenShift Allowed registries are configured:

scan8

Looking at this result in-detail, I can see that the compliance operator has pulled down the OpenShift config from the API, and determined that it is missing the .spec.registry.allowedRegistries config, which would limit the registries images are able to be pulled from.

scan9
scan10

Wrap up

In this article I've taken a closer look at the Asset Reporting Format (ARF), and how you can use ARF to create actionable reports about platform security controls.

I looked at a couple of examples of ARF "in action":

  • Creating ARF reports about Red Hat Enterprise Linux (RHEL) security controls, and showing which controls have failed.
  • Creating ARF reports using the OpenShift Compliance Operator, and extracting raw ARF and identifying failed controls.

If you want to take a closer look at ARF you can find the schema and docs available at the NIST portal: https://csrc.nist.gov/projects/security-content-automation-protocol/specifications/arf

Thanks for reading!