Skip to content

Commit

Permalink
Implement opr:oscal-version and v:compare functions. (usnistgov#1420)
Browse files Browse the repository at this point in the history
The opr:oscal-version function meets the "req-meta-oscal-version"
requirement from usnistgov#1386.

The v:compare utility function helps opr:oscal-version meet the
"req-meta-oscalversion-error" requirement.
  • Loading branch information
galtm authored and aj-stein-nist committed Jul 10, 2023
1 parent 22f423e commit e5fa65f
Show file tree
Hide file tree
Showing 4 changed files with 916 additions and 71 deletions.
54 changes: 38 additions & 16 deletions src/utils/util/resolver-pipeline/oscal-profile-resolve-metadata.xsl
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
<xsl:stylesheet version="3.0"
xmlns="http://csrc.nist.gov/ns/oscal/1.0"
xmlns:mh="http://csrc.nist.gov/ns/message"
xmlns:o="http://csrc.nist.gov/ns/oscal/1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:opr="http://csrc.nist.gov/ns/oscal/profile-resolution"
xmlns:u="http://csrc.nist.gov/ns/uuid"
exclude-result-prefixes="xs math o opr u"
xmlns:v="http://csrc.nist.gov/ns/version"
exclude-result-prefixes="xs math o opr u v"
xpath-default-namespace="http://csrc.nist.gov/ns/oscal/1.0" >

<!-- XSLT 2.0 so as to validate against XSLT 3.0 constructs -->


<!-- How to specify top-level UUID for result catalog:
Expand All @@ -27,6 +28,9 @@
$uuid-method, $top-uuid, and $uuid-service. -->
<xsl:import href="uuid-method-choice.xsl"/>

<!-- version-util.xsl has semantic version comparison. -->
<xsl:import href="version-util.xsl"/>

<!-- Top-level UUID for result catalog. -->
<xsl:param name="top-uuid-computed" as="xs:string">
<xsl:call-template name="u:determine-uuid"/>
Expand All @@ -38,9 +42,6 @@
metadata, due to privacy or security concerns. This parameter is
passed from oscal-profile-RESOLVE.xsl and the end user can override it. -->
<xsl:param name="hide-source-profile-uri" as="xs:boolean" select="false()"/>

<!-- Version of this resolution tool -->
<xsl:variable name="tool-oscal-version" as="xs:string" select="'1.1.0'"/>

<xsl:variable name="source-profile" as="xs:string"
select="if ($hide-source-profile-uri) then 'profile' else $profile-origin-uri"/>
Expand Down Expand Up @@ -84,21 +85,42 @@
<xsl:copy>
<xsl:sequence select="opr:oscal-version(
.,
ancestor::profile/selection/metadata/oscal-version/normalize-space(),
$tool-oscal-version
ancestor::profile/selection/metadata/oscal-version/normalize-space()
)"/>
</xsl:copy>
</xsl:template>

<!-- If there is a common major version among all inputs,
return the most recent minor version or the tool version,
whichever is earlier.
If there is no common major version, return fatal error. -->
<xsl:function name="opr:oscal-version" as="xs:string">
<!-- Return the oscal-version of the source profile.
Perform error checking as in requirement
"req-meta-oscalversion-error".
-->
<xsl:function name="opr:oscal-version" as="xs:string" visibility="public">
<!-- Without visiblity="public" the XSpec test returns
XTDE0041: Cannot invoke function opr:oscal-version#2 externally, because it is not public
-->
<xsl:param name="source" as="xs:string"/>
<xsl:param name="imported" as="xs:string*"/>
<xsl:param name="tool" as="xs:string"/>
<xsl:value-of select="'TODO: Not implemented yet'"/>

<xsl:variable name="options" as="map(*)"
select="map{
'normalize-space': true(),
'supply-missing-zeros': true(),
'remove-leading-zeros': true(),
'allow-empty-string': true()
}"/>
<xsl:for-each select="$imported">
<xsl:if test="v:compare($source, ., $options) eq xs:integer(-1)">
<xsl:call-template name="mh:message-handler">
<xsl:with-param name="message-type" select="'Error'"/>
<xsl:with-param name="terminate" select="true()"/>
<xsl:with-param name="text" expand-text="yes">Import uses oscal-version of {.
}, which is newer than profile oscal-version of {
$source}.</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
<!-- Return the version from the source parameter. -->
<xsl:value-of select="$source"/>
</xsl:function>

<!--<xsl:template match="selection" mode="imported-metadata">
Expand Down
79 changes: 24 additions & 55 deletions src/utils/util/resolver-pipeline/testing/2_metadata/metadata.xspec
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@
<oscal-version>...</oscal-version>
</x:expect>
</x:scenario>
<x:scenario label="Sanity checks for calling opr:oscal-version with correct inputs: " pending="opr:oscal-version not implemented yet">
<x:scenario label="Most recent version is in selection metadata">
<x:scenario label="Sanity checks for calling opr:oscal-version with correct inputs: ">
<x:scenario label="Most recent version is in selection metadata" catch="yes">
<x:context select="/o:profile/o:metadata/o:oscal-version">
<profile>
<metadata>
Expand All @@ -252,9 +252,8 @@
</selection>
</profile>
</x:context>
<x:expect label="Most recent minor version mentioned in document">
<oscal-version>1.0.4</oscal-version>
</x:expect>
<x:expect label="Error"
test="$x:result instance of map(*) and $x:result('err') instance of map(*)"/>
</x:scenario>
<x:scenario label="Most recent version is in profile metadata">
<x:context select="/o:profile/o:metadata/o:oscal-version">
Expand All @@ -269,81 +268,51 @@
</selection>
</profile>
</x:context>
<x:expect label="Most recent minor version mentioned in document">
<x:expect label="Version from profile">
<oscal-version>1.0.4</oscal-version>
</x:expect>
</x:scenario>
<x:scenario label="Tool version is older than document versions">
<x:context select="/o:profile/o:metadata/o:oscal-version">
<profile>
<metadata>
<oscal-version>1.4.0</oscal-version>
</metadata>
<selection>
<metadata>
<oscal-version>1.2.0</oscal-version>
</metadata>
</selection>
</profile>
</x:context>
<x:expect label="Tool version">
<oscal-version>1.1.0</oscal-version>
</x:expect>
</x:scenario>
</x:scenario>
</x:scenario>

<x:scenario label="Tests for opr:oscal-version function" pending="opr:oscal-version not implemented yet">
<x:scenario label="Tests for opr:oscal-version function">
<x:scenario label="Same major version">
<x:scenario label="Source profile version newer than imported document">
<x:scenario label="Source profile version at least as new as all imported documents">
<x:call function="opr:oscal-version">
<x:param name="source" select="'1.0.1'"/>
<x:param name="imported" select="('1.0.0','1.0.0')"/>
<x:param name="tool" select="'1.0.1'"/>
<x:param name="imported" select="('1.0.0','1.0.1')"/>
</x:call>
<x:expect label="Newest minor version" select="'1.0.1'"/>
<x:expect label="Source version and no error" select="'1.0.1'"/>
</x:scenario>
<x:scenario label="Source profile version older than imported document">
<x:scenario label="Source profile version older than at least one imported document" catch="yes">
<x:call function="opr:oscal-version">
<x:param name="source" select="'1.0.0'"/>
<x:param name="imported" select="('1.0.1','1.0.1')"/>
<x:param name="tool" select="'1.0.1'"/>
<x:param name="imported" select="('1.0.0','1.0.1')"/>
</x:call>
<x:expect label="Newest minor version" select="'1.0.1'"/>
<x:expect label="Error"
test="$x:result instance of map(*) and $x:result('err') instance of map(*)"/>
</x:scenario>
<x:scenario label="Tool version is older">
<x:scenario label="Imported document has a newer pre-release identifier" catch="yes">
<x:call function="opr:oscal-version">
<x:param name="source" select="'1.0.1'"/>
<x:param name="imported" select="('1.0.1','1.0.1')"/>
<x:param name="tool" select="'1.0.0'"/>
<x:param name="source" select="'1.0.1-rc1'"/>
<x:param name="imported" select="('1.0.0','1.0.1-rc2')"/>
</x:call>
<x:expect label="Tool version" select="'1.0.0'"/>
<x:expect label="Error"
test="$x:result instance of map(*) and $x:result('err') instance of map(*)"/>
</x:scenario>
</x:scenario>
<x:scenario label="Different major version">
<x:scenario label="Source profile version newer than imported document">
<x:scenario label="Source profile version at least as new as all imported documents">
<x:call function="opr:oscal-version">
<x:param name="source" select="'2.0.1'"/>
<x:param name="imported" select="('1.0.0','1.0.0')"/>
<x:param name="tool" select="'2.0.1'"/>
<x:param name="imported" select="('1.37.40','1.37')"/>
</x:call>
<x:expect label="Error"
test="$x:result instance of map(*) and $x:result('err') instance of map(*)"/>
<x:expect label="Source version and no error" select="'2.0.1'"/>
</x:scenario>
<x:scenario label="Source profile version older than imported document">
<x:scenario label="Source profile version older than at least one imported document" catch="yes">
<x:call function="opr:oscal-version">
<x:param name="source" select="'1.0.0'"/>
<x:param name="imported" select="('2.0.1','2.0.1')"/>
<x:param name="tool" select="'2.0.1'"/>
</x:call>
<x:expect label="Error"
test="$x:result instance of map(*) and $x:result('err') instance of map(*)"/>
</x:scenario>
<x:scenario label="Tool version is older">
<x:call function="opr:oscal-version">
<x:param name="source" select="'2.0.1'"/>
<x:param name="imported" select="('2.0.1','2.0.1')"/>
<x:param name="tool" select="'1.0.0'"/>
<x:param name="source" select="'1.37.40'"/>
<x:param name="imported" select="('2.0','1.0.1')"/>
</x:call>
<x:expect label="Error"
test="$x:result instance of map(*) and $x:result('err') instance of map(*)"/>
Expand Down
Loading

0 comments on commit e5fa65f

Please sign in to comment.