Skip to content

AllowedValueCollectingNodeItemVisitor throws exceptions for allowed values referencing vars bound to empty sequences #112

@aj-stein-gsa

Description

@aj-stein-gsa

Describe the bug

In recent versions of the liboscal-java, the dependent oscal-cli's list-allowed-values subcommand to dump values fails with any external constraint set defined in a file with a let/@var and its @expression. They are bound to the model but processed without validating constraints against an instance, so most such constraints evaluate to an empty sequence and are missing from the dynamic context.

Who is the bug affecting

Developers using the existing liboscal-java functionality or extending it to dump information about inline or external constraints an inventory for documentation or further analysis.

How do we replicate this issue

Run the following unit test (cross-referenced in a branch by issue number below as well).

/*
 * SPDX-FileCopyrightText: none
 * SPDX-License-Identifier: CC0-1.0
 */

package gov.nist.secauto.oscal.lib.model.util;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import edu.umd.cs.findbugs.annotations.NonNull;
import gov.nist.secauto.metaschema.core.metapath.item.node.IDefinitionNodeItem;
import gov.nist.secauto.metaschema.core.model.MetaschemaException;
import gov.nist.secauto.metaschema.core.model.constraint.ExternalConstraintsModulePostProcessor;
import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue;
import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValuesConstraint;
import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet;
import gov.nist.secauto.metaschema.core.model.xml.IXmlMetaschemaModule;
import gov.nist.secauto.metaschema.core.model.xml.ModuleLoader;
import gov.nist.secauto.metaschema.core.model.xml.XmlMetaConstraintLoader;
import gov.nist.secauto.metaschema.core.util.CollectionUtil;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
import gov.nist.secauto.metaschema.databind.model.IBoundModule;
import gov.nist.secauto.oscal.lib.OscalBindingContext;
import gov.nist.secauto.oscal.lib.model.OscalCompleteModule;
import gov.nist.secauto.oscal.lib.model.util.AllowedValueCollectingNodeItemVisitor.AllowedValuesRecord;
import gov.nist.secauto.oscal.lib.model.util.AllowedValueCollectingNodeItemVisitor.NodeItemRecord;

class AbstractNodeItemVisitorTest {

  @Test
  void testAllowedValuesMissingVariableBindings() throws MetaschemaException, IOException {
	    List<IConstraintSet> constraintSet = new XmlMetaConstraintLoader().load(ObjectUtils.requireNonNull(ObjectUtils.notNull(Paths.get("src/test/resources/content/computer-metaschema-meta-constraints.xml").toUri())));
        ExternalConstraintsModulePostProcessor postProcessor = new ExternalConstraintsModulePostProcessor(constraintSet);
        ModuleLoader loader = new ModuleLoader(CollectionUtil.singletonList(postProcessor));
        IXmlMetaschemaModule module = loader.load(ObjectUtils.notNull(Paths.get("src/test/resources/content/computer-example.xml").toUri()));
        AllowedValueCollectingNodeItemVisitor walker = new AllowedValueCollectingNodeItemVisitor();
        walker.visit(module);
        Collection<NodeItemRecord> allowedValuesByTarget = ObjectUtils.notNull(walker.getAllowedValueLocations());
        assertEquals(1, allowedValuesByTarget.size());
  }
}

computer-example.xml:

<?xml version="1.0" encoding="UTF-8"?>
<METASCHEMA xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://csrc.nist.gov/ns/oscal/metaschema/1.0 ../schema/xml/metaschema.xsd"
 xmlns="http://csrc.nist.gov/ns/oscal/metaschema/1.0">
    <schema-name>Minimak Computer Model</schema-name>
    <schema-version>0.0.5</schema-version>
    <short-name>computer</short-name>
    <namespace>http://example.com/ns/computer</namespace>
    <json-base-uri>http://example.com/ns/computer</json-base-uri>
    <define-assembly name="computer">
        <formal-name>Computer Assembly</formal-name>
        <description>A minimal computer model to test let var usage with external constraints.</description>
        <root-name>computer</root-name>
        <define-flag name="id" required="yes"/>
    </define-assembly>
</METASCHEMA>

computer-metaschema-meta-constraints.xml:

<?xml version="1.0" encoding="UTF-8"?>
<metaschema-meta-constraints xmlns="http://csrc.nist.gov/ns/oscal/metaschema/1.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://csrc.nist.gov/ns/oscal/metaschema/1.0 ../../../../core/metaschema/schema/xml/metaschema-meta-constraints.xsd">
    <context>
    	<metapath target="/computer"/>
    	<constraints>
            <let var="computer-id" expression="@id"/>
            <allowed-values id="example-constraint-using-let-var" target="$computer-id">
            	<enum value="model1"/>
            	<enum value="model2"/>
            	<enum value="model3"/>
            </allowed-values>
    	</constraints>
    </context>
</metaschema-meta-constraints>

Resulting exception:

gov.nist.secauto.metaschema.core.metapath.MetapathException: An error occurred while evaluating the expression '$computer-id'. MPST0008: Variable 'computer-id' not defined in the dynamic context.
	at gov.nist.secauto.metaschema.core.metapath.MetapathExpression.evaluate(MetapathExpression.java:192)
	at gov.nist.secauto.metaschema.core.metapath.impl.LazyCompilationMetapathExpression.evaluate(LazyCompilationMetapathExpression.java:66)
	at gov.nist.secauto.oscal.lib.model.util.AllowedValueCollectingNodeItemVisitor.lambda$0(AllowedValueCollectingNodeItemVisitor.java:60)
	at java.base/java.util.LinkedList$LLSpliterator.forEachRemaining(LinkedList.java:1249)
	at java.base/java.util.stream.ReferencePipeline$Head.forEachOrdered(ReferencePipeline.java:772)
	at gov.nist.secauto.oscal.lib.model.util.AllowedValueCollectingNodeItemVisitor.handleAllowedValuesAtLocation(AllowedValueCollectingNodeItemVisitor.java:58)
	at gov.nist.secauto.oscal.lib.model.util.AllowedValueCollectingNodeItemVisitor.visitAssembly(AllowedValueCollectingNodeItemVisitor.java:99)
	at gov.nist.secauto.oscal.lib.model.util.AllowedValueCollectingNodeItemVisitor.visitAssembly(AllowedValueCollectingNodeItemVisitor.java:1)
	at gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem.accept(IAssemblyNodeItem.java:76)
	at gov.nist.secauto.metaschema.core.metapath.item.node.AbstractNodeItemVisitor.visitModelChildren(AbstractNodeItemVisitor.java:73)
	at gov.nist.secauto.metaschema.core.metapath.item.node.AbstractNodeItemVisitor.visitMetaschema(AbstractNodeItemVisitor.java:185)
	at gov.nist.secauto.oscal.lib.model.util.AllowedValueCollectingNodeItemVisitor.visit(AllowedValueCollectingNodeItemVisitor.java:51)
	at gov.nist.secauto.oscal.lib.model.util.AllowedValueCollectingNodeItemVisitor.visit(AllowedValueCollectingNodeItemVisitor.java:46)
	at gov.nist.secauto.oscal.lib.model.util.AbstractNodeItemVisitorTest.testAllowedValuesMissingVariableBindings(AbstractNodeItemVisitorTest.java:93)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: gov.nist.secauto.metaschema.core.metapath.StaticMetapathException: MPST0008: Variable 'computer-id' not defined in the dynamic context.
	at gov.nist.secauto.metaschema.core.metapath.DynamicContext.getVariableValue(DynamicContext.java:284)
	at gov.nist.secauto.metaschema.core.metapath.cst.VariableReference.evaluate(VariableReference.java:71)
	at gov.nist.secauto.metaschema.core.metapath.cst.AbstractExpression.accept(AbstractExpression.java:47)
	at gov.nist.secauto.metaschema.core.metapath.MetapathExpression.evaluate(MetapathExpression.java:190)
	... 16 more

Expected behavior (i.e. solution)

Variables bindings are evaluated to empty expressions and do not throw exceptions.

Other comments

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingjavaPull requests that update Java code

Type

No type

Projects

Status

Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions