Skip to content

Conversation

@ahouseholder
Copy link
Contributor

@ahouseholder ahouseholder commented Jun 23, 2025

This PR creates a DecisionTable object that contains:

  • a dictionary of decision points to be used as inputs
  • an outcome group (technically a pointer to a decision point object in the aforementioned dictionary) to be used as output
  • a mapping of combinations of decision point values to outcome values

While the DataTable object can generate a CSV file as output, importing of CSV tables to populate a DecisionTable object is not supported in this implementation.

This PR is just intended to provide the base data structure because other work depends on the existence of this object.

Incidental Changes

  • Changes to data/json/* are incidental to changes in the base object ordering or the addition of a new basic namespace to capture decision points and outcomes that represent generic prioritization concepts (MoSCoW, Yes/No, Eisenhower matrix, etc.) The decision points already existed, their location may have changed.

  • Changes to data/schema/* reflect updates to the data objects, and the fact that ssvc.doctools has been updated to auto-generate schemas from the pydantic data objects.

  • Changes to docs/**.md are largely due to site build-time dynamic content generation based on code that has been reorganized in the ssvc package. These likely broke in Refactor namespaces #791.

Future work not addressed in this PR:

CoPilot Summary

This pull request introduces several updates across the codebase, focusing on upgrading dependencies, enhancing Docker-based testing workflows, and adding or updating decision point JSON schemas to align with schema version 2.0.0. The most important changes are summarized below:

Dependency and Workflow Updates:

  • Updated the Python version in the GitHub Actions workflow (.github/workflows/link_checker.yml) from 3.10 to 3.12 for compatibility with the latest features.
  • Enhanced the Makefile by adding a step to build the latest Docker test image before running tests, improving the reliability of Docker-based testing.

New Decision Point JSON Schemas:

  • Added a new decision point schema for "Do, Schedule, Delegate, Delete" (do_schedule_delegate_delete_1_0_0.json) under the basic namespace.
  • Added a new decision point schema for "MoSCoW" (moscow_1_0_0.json) under the basic namespace.
  • Added a new decision point schema for "Value, Complexity" (value_complexity_1_0_0.json) under the basic namespace.
  • Added a new decision point schema for "YesNo" (yesno_1_0_0.json) under the basic namespace.

Updates to Existing JSON Schemas:

  • Updated multiple existing decision point schemas across various namespaces (cisa, cvss, etc.) to use schemaVersion: 2.0.0, ensuring consistency and compatibility with the latest schema standards. This includes files such as cisa_levels_1_0_0.json [1] access_complexity_1_0_0.json [2] and authentication_1_0_0.json [3] among others.

@ahouseholder ahouseholder linked an issue Jun 23, 2025 that may be closed by this pull request
@sei-vsarvepalli
Copy link
Contributor

I think the Decision TableJSON schema mappings field should match the selections in ../data/schema/current/Decision_Point_Value_Selection.schema.json - so basically people should be able go from selections in the a DP Value Schema and iterate to find all the possible outcomes. I think the CSV is also very noisy, the column headers have say ssvc:SI:1.0.0, each row also has the same column name is repeated. As the Column is known already the header, there is no reason to repeat it ssvc:SI:1.0.0:FR

@ahouseholder
Copy link
Contributor Author

I think the Decision TableJSON schema mappings field should match the selections in ../data/schema/current/Decision_Point_Value_Selection.schema.json - so basically people should be able go from selections in the a DP Value Schema and iterate to find all the possible outcomes.

If I'm interpreting the schema right, I don't think this will work.

  1. a selection object is basically just a DecisionPoint object with a (potentially) reduced set of values:
class SsvcDecisionPointSelection(BaseModel):
    name: str = Field(..., min_length=1, description="Name of the decision point")
    namespace: str = Field(
        ..., min_length=1, description="Namespace of the decision point"
    )
    version: str = Field(..., min_length=1, description="Version of the decision point")
    values: List[str] = Field(
        ..., min_items=1, description="Selected values for the decision point"
    )

This doesn't fit as a mapping because it doesn't reflect the specific mapping of ((d1v1, d2v1, d3v1), o1v1) etc. For example, https://github.com/CERTCC/SSVC/blob/main/data/schema_examples/CVE-1900-1234-Decision_Point_Value_Selection.json does not even mention an outcome value. So this object cannot be used to describe a policy mapping.

  1. A decision point value selection object:
class DecisionPointValueSelection(BaseModel):
    id: str = Field(
        ...,
        min_length=1,
        description="Identifier for the vulnerability (e.g., CVE, VU#, GHSA, etc.)",
    )
    role: str = Field(
        ...,
        min_length=1,
        description="Stakeholder role (Supplier, Deployer, Coordinator, etc.)",
    )
    timestamp: datetime = Field(
        ..., description="RFC 3339 timestamp for the evaluation"
    )
    schemaVersion: str = Field(
        ...,
        min_length=1,
        description="Schema version used for the decision point evaluation",
    )
    selections: List[SsvcDecisionPointSelection] = Field(
        ..., min_items=1, description="Evaluated decision points"
    )

However, the fields id, role, timestamp, and schema version are out of place for a decision policy, as they are specific to a particular evaluation of a specific vul at some point in time by a given role. The only thing left here is the "selections", but see item 1 above for why that's not going to work.

I'm also suggesting we punt the "How to represent selection" problem to

I think the CSV is also very noisy, the column headers have say ssvc:SI:1.0.0, each row also has the same column name is repeated. As the Column is known already the header, there is no reason to repeat it ssvc:SI:1.0.0:FR

Agreed. I will look into maybe passing this through a Pandas dataframe before we emit the CSV so we can make it a little less chatty.

@sei-vsarvepalli
Copy link
Contributor

sei-vsarvepalli commented Jun 30, 2025

I think the Decision TableJSON schema mappings field should match the selections in ../data/schema/current/Decision_Point_Value_Selection.schema.json - so basically people should be able go from selections in the a DP Value Schema and iterate to find all the possible outcomes.

If I'm interpreting the schema right, I don't think this will work.

  1. a selection object is basically just a DecisionPoint object with a (potentially) reduced set of values:
class SsvcDecisionPointSelection(BaseModel):
    name: str = Field(..., min_length=1, description="Name of the decision point")
    namespace: str = Field(
        ..., min_length=1, description="Namespace of the decision point"
    )
    version: str = Field(..., min_length=1, description="Version of the decision point")
    values: List[str] = Field(
        ..., min_items=1, description="Selected values for the decision point"
    )

This doesn't fit as a mapping because it doesn't reflect the specific mapping of ((d1v1, d2v1, d3v1), o1v1) etc. For example, https://github.com/CERTCC/SSVC/blob/main/data/schema_examples/CVE-1900-1234-Decision_Point_Value_Selection.json does not even mention an outcome value. So this object cannot be used to describe a policy mapping.

  1. A decision point value selection object:
class DecisionPointValueSelection(BaseModel):
    id: str = Field(
        ...,
        min_length=1,
        description="Identifier for the vulnerability (e.g., CVE, VU#, GHSA, etc.)",
    )
    role: str = Field(
        ...,
        min_length=1,
        description="Stakeholder role (Supplier, Deployer, Coordinator, etc.)",
    )
    timestamp: datetime = Field(
        ..., description="RFC 3339 timestamp for the evaluation"
    )
    schemaVersion: str = Field(
        ...,
        min_length=1,
        description="Schema version used for the decision point evaluation",
    )
    selections: List[SsvcDecisionPointSelection] = Field(
        ..., min_items=1, description="Evaluated decision points"
    )

However, the fields id, role, timestamp, and schema version are out of place for a decision policy, as they are specific to a particular evaluation of a specific vul at some point in time by a given role. The only thing left here is the "selections", but see item 1 above for why that's not going to work.

I'm also suggesting we punt the "How to represent selection" problem to

I think the CSV is also very noisy, the column headers have say ssvc:SI:1.0.0, each row also has the same column name is repeated. As the Column is known already the header, there is no reason to repeat it ssvc:SI:1.0.0:FR

Agreed. I will look into maybe passing this through a Pandas dataframe before we emit the CSV so we can make it a little less chatty.

To be clear I mean the Decision Table JSON format will be hard to punt it off to use #800 in my mind. I think if we can basically mimic the ../data/schema/current/Decision_Point_Value_Selection.schema.json#/$defs/SsvcdecisionpointselectionSchema section of the schema for this Class it will help other things fall in place including the CSV.

So for example for Deployer decision table the very first row of CSV will look like this (basically matches . While it is noisy it will capture the each column of the row in "gory" details - so one can capture the matching row-column combo to find all the DSOI matching values in this example.

            {
                "namespace": "ssvc",
                "version": "1.0.0",
                "values": [
                    "none"
                ],
                "name": "Exploitation"
            },
            {
                "namespace": "ssvc",
                "version": "1.0.1",
                "values": [
                    "small"
                ],
                "name": "Exposure"
            },
            {
                "namespace": "ssvc",
                "version": "2.0.0",
                "values": [
                    "no"
                ],
                "name": "Automatable"
            },
            {
                "namespace": "ssvc",
                "version": "1.0.0",
                "values": [
                    "low"
                ],
                "name": "Human Impact"
            },
            {
                "namespace": "ssvc",
                "version": "1.0.0",
                "values": [
                    "defer"
                ],
                "name": "Defer, Scheduled, Out-of-Cycle, Immediate"
            }
            

This will be the first row in the older CSV deployer-options.csv

Exploitation,Exposure,Automatable,Human Impact,Priority
none,small,no,low,defer

or the newer CSV

ssvc:E:1.0.0,ssvc:EXP:1.0.0,ssvc:A:2.0.0,ssvc:HI:2.0.1,ssvc:DSOI:1.0.0
N,S,N,L,D

Hope that makes sense.

@ahouseholder ahouseholder added the tech/backend Back-end tools, code, infrastructure label Jul 1, 2025
@ahouseholder ahouseholder modified the milestones: 2025-06, 2025-09 Jul 1, 2025
@sei-vsarvepalli
Copy link
Contributor

When I try to this test on the Coordinator Publish and Coordinator Triage - the values as represented fails due to the error

Traceback (most recent call last):
  File "/Users/vssarvepalli/Development/SSVC/venv3.13/coordinator_triage.py", line 67, in <module>
    PRIORITY_1 = DecisionTable(
            name = "Coordinator Triage Decision Table",
    ...<5 lines>...
            mapping = maps
    )
  File "/Users/vssarvepalli/Development/SSVC/venv3.13/lib/python3.13/site-packages/pydantic/main.py", line 253, in __init__
    validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 1 validation error for DecisionTable
  Value error, Topological order check failed. See logs for details. [type=value_error, input_value={'name': 'Coordinator Tri...schemaVersion': '2.0.0'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.11/v/value_error

coordinator_publish_mapping.json
coordinator_triage_mapping.json


class ValuedVersion(BaseModel):
version: VersionString
obj: _GenericSsvcObject
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The obj: _GenericSsvcObject lines are a problem because it's not copying the full object, just the fields that are explicitly in the _GenericSsvcObject mixin. We need to resolve this before merging.

@sei-vsarvepalli
Copy link
Contributor

Also CVSS Equivalency Set to CVSS LMHC scoring also fails the pydantic order validation test. The sorted array is enclosed here for testing.

cvss_v4_mapping.json

@sei-vsarvepalli
Copy link
Contributor

There is a problem with SSVC registry of Decision Points. It looks like the values field under Decision Point is object type with the key being the name part of the name/value pair. However, the JSON Object type is an unordered collection of name/value pairs. This is not suitable for the Decision Point Values which we want to treat as ordered set.

"values": {
"M": {
"key": "M",
"name": "Minimal",
"description": "Neither Support nor Essential apply. The vulnerable component may be used within the entities, but it is not used as a mission-essential component, nor does it provide impactful support to mission-essential functions."
},
"S": {
"key": "S",
"name": "Support",
"description": "The vulnerable component only supports MEFs for two or more entities."
},
"E": {
"key": "E",
"name": "Essential",
"description": "The vulnerable component directly provides capabilities that constitute at least one MEF for at least one entity; component failure may (but does not necessarily) lead to overall mission failure."
}
}

# subject to its own license.
# DM24-0278

from ssvc.decision_tables.base import decision_table_to_longform_df
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whe I try to run this in the relative path of the repository it fails as there is also another ssvc directory under here in "./" of this path. Basically the src/ssvc/decision_tables/ssvc is in the preferred order for python file execution over the src/ssvc path.

(venv) Mac-redjayapple:SSVC vssarvepalli$ python src/ssvc/decision_tables/base.py 
Traceback (most recent call last):
  File "/Users/vijay/Development/SSVC/src/ssvc/decision_tables/base.py", line 31, in <module>
    from ssvc._mixins import _Base, _Commented, _Namespaced, _SchemaVersioned, _Versioned
ModuleNotFoundError: No module named 'ssvc._mixins'
(venv) Mac-redjayapple:SSVC vssarvepalli$ 

@ahouseholder ahouseholder merged commit 1fb7e2a into main Aug 5, 2025
5 checks passed
@ahouseholder ahouseholder deleted the feature/592-we-need-a-policy-object branch August 5, 2025 18:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment