Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions docs/adr/0012-ssvc-namespaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ consulted: @tschmidtb51
## Context and Problem Statement

We need to include decision points and other objects that are not directly
defined by the SSVC project team. For example, CVSS vector elements are a
defined by the SSVC project team. For example, CVSS vector elements are a
rich source of structured data that can be used to inform SSVC decisions and
modeled as SSVC decision point objects. However, the
[FIRST CVSS SIG](https://www.first.org/cvss) owns the definition of CVSS vector
modeled as SSVC decision point objects. However, the
[FIRST CVSS SIG](https://www.first.org/cvss) owns the definition of CVSS vector
elements. So we need a way to describe these objects in SSVC format
without making them part of the SSVC specification.


## Decision Drivers

- Need to include decision points based on data, objects, standards, and other
Expand Down Expand Up @@ -49,7 +48,7 @@ based on other sources).
main project. We use the `cvss` namespace to contain CVSS vector elements.

**Unregistered namespaces** for objects that we do not create or maintain, but
that others may want for their own use. Unregistered namespaces must start with
that others may want for their own use. Unregistered namespaces must start with
an `x_` prefix followed by a reverse domain name, such as `x_org.example`.
Unregistered namespaces are intended for experimental or private use.

Expand All @@ -61,10 +60,10 @@ Unregistered namespaces are intended for experimental or private use.
register their namespace with the SSVC project.

**Namespace extensions** for objects that are derived from other objects in an
registered or unregistered namespace. Extensions are not intended to be used to
registered or unregistered namespace. Extensions are not intended to be used to
introduce new objects, but rather to refine existing objects with additional data
or semantics.
Namespace extensions can be used for refining the meaning of decision point
Namespace extensions can be used for refining the meaning of decision point
values for a specific constituency, or adding additional nuance to
interpretation of a decision point in a specific context.

Expand All @@ -84,7 +83,6 @@ interpretation of a decision point in a specific context.
- Facilitates language translation and localization of SSVC objects to specific
constituencies


#### Negative Consequences

- Registered namespaces must be managed and maintained
Expand Down Expand Up @@ -114,7 +112,6 @@ SSVC specification under our control and objects that were derived from other so
- Bad, because it made it difficult to distinguish between SSVC project objects and
objects based on specifications we neither created nor maintained


<!-- This is an optional element. Feel free to remove. -->
## More Information

Expand Down
29 changes: 11 additions & 18 deletions docs/reference/code/namespaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

We use namespaces in SSVC to organize the various components of the framework.
The bulk of our work is done in the `ssvc` namespace, which contains the core
decision points for SSVC.
decision points for SSVC.

!!! question "Why does SSVC need namespaces?"

Expand Down Expand Up @@ -54,7 +54,6 @@ subgraph base_ns[Base Namespace]
end
```


!!! info inline end "Current Registered Namespaces"

The SSVC project currently has a set of registered namespaces that are
Expand All @@ -76,7 +75,7 @@ A list of the current registered namespaces can be found in the sidebar.

Registered namespaces are intended to be used as follows:

- Objects in the `ssvc` namespace are managed by the SSVC
- Objects in the `ssvc` namespace are managed by the SSVC
project team. We have complete control over these ones.
- Objects in other explicitly registered namespaces are provided for convenience,
but the SSVC team is not responsible for modifying the content or semantics of
Expand Down Expand Up @@ -112,8 +111,6 @@ Registered namespaces are intended to be used as follows:
Suggestions for changes to the CVSS specifications should be directed to the
[FIRST CVSS Special Interest Group](https://www.first.org/cvss/) (SIG).



!!! example "Potential Standards-based namespaces"

We may in the future add namespaces when needed to reflect different standards
Expand All @@ -140,8 +137,8 @@ we expect that this will rarely lead to conflicts in practice.
- Unregistered namespaces must use the `x_` prefix.
- Following the `x_` prefix, unregistered namespaces must use reverse domain name notation of a domain under their control to ensure uniqueness.
- Aside from the required `x_` prefix, unregistered namespaces must contain only alphanumeric characters, dots (`.`), and dashes (`-`).
- For any domain using other characters, DNS Punycode must be used

- For any domain using other characters, DNS Punycode must be used

!!! warning "Namespace Conflicts"

Expand All @@ -150,7 +147,6 @@ we expect that this will rarely lead to conflicts in practice.
`x_example.test`, and there are no guarantees of global uniqueness for the
decision points in the `x_example.test` namespace.


!!! tip "Test Namespace"

The `x_example.test` namespace is used for testing purposes and is not intended for production use.
Expand Down Expand Up @@ -198,6 +194,7 @@ A single fragment identifier (`#`) may be included in an extension segment, but
Fragment segments can be used to indicate a specific interpretation or context for the extension.
Note: Without a fragment segment, all decision points of an organization fall into one bucket, which is in most cases not intended. Therefore, the use of a fragment segment is recommended.
The following diagram illustrates the structure of namespace extensions:

```mermaid
---
title: Namespace Extensions
Expand Down Expand Up @@ -256,7 +253,6 @@ base_ns -->|/| first
available in the default language (`en-US`), and (b) that this extension has
been translated into German (Germany).


!!! example "Use of fragment identifiers and language tags"

Imagine an Information Sharing and Analysis Organization (ISAO) `isao.example`
Expand Down Expand Up @@ -287,9 +283,9 @@ base_ns -->|/| first

### Usage Suggestions

Although we reserved the first segment of the extension for language tags,
Although we reserved the first segment of the extension for language tags,
there are scenarios where it may be appropriate to use a language tag in a later
segment of the extension.
segment of the extension.

!!! tip "Use BCP-47 Language Tags"

Expand All @@ -314,7 +310,6 @@ segment of the extension.
For example, if your organization is `example.com`, you might use an extension
like `ssvc//com.example#extension`.


## Technical requirements

The following technical requirements are enforced for SSVC namespaces,
Expand Down Expand Up @@ -350,17 +345,15 @@ based on the implementation in `src/ssvc/namespaces.py` and the NS_PATTERN regul
- If any extension segments are present, the following rules apply:
- The first extension segment must be a valid BCP-47 language tag or empty (i.e., `//`).
- Subsequent extension segments:
- must start with a letter (upper or lowercase)
- may contain letters, numbers, dots (`.`), hyphens (`-`), and at most one hash (`#`)
- must not contain consecutive dots or hyphens (no `..`, `--`, `.-`, `-.`, `---`, etc.)
- if a hash is present, it separates the main part from an optional fragment part
- are separated by single forward slashes (`/`)
- must start with a letter (upper or lowercase)
- may contain letters, numbers, dots (`.`), hyphens (`-`), and at most one hash (`#`)
- must not contain consecutive dots or hyphens (no `..`, `--`, `.-`, `-.`, `---`, etc.)
- if a hash is present, it separates the main part from an optional fragment part
- are separated by single forward slashes (`/`)
- Multiple extension segments are allowed


## The `ssvc.namespaces` module

The `ssvc.namespaces` module provides a way to access and use these namespaces.

::: ssvc.namespaces

23 changes: 20 additions & 3 deletions src/ssvc/_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@
from datetime import datetime, timezone
from typing import Any, Optional

from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from pydantic import (
BaseModel,
ConfigDict,
Field,
field_validator,
model_validator,
)
from semver import Version

from ssvc.namespaces import NameSpace
Expand Down Expand Up @@ -64,7 +70,9 @@ class _SchemaVersioned(BaseModel):
Mixin class for version
"""

schemaVersion: str = Field(..., description="Schema version of the SSVC object")
schemaVersion: str = Field(
..., description="Schema version of the SSVC object"
)

@model_validator(mode="before")
def set_schema_version(cls, data):
Expand Down Expand Up @@ -118,7 +126,16 @@ class _Keyed(BaseModel):
"(`T*` is explicitly grandfathered in as a valid key, but should not be used for new objects.)",
pattern=r"^(([a-zA-Z0-9])|([a-zA-Z0-9][a-zA-Z0-9_]*[a-zA-Z0-9])|(T\*))$",
min_length=1,
examples=["E", "A", "SI", "L", "M", "H", "Mixed_case_OK", "alph4num3ric"],
examples=[
"E",
"A",
"SI",
"L",
"M",
"H",
"Mixed_case_OK",
"alph4num3ric",
],
)


Expand Down
8 changes: 6 additions & 2 deletions src/ssvc/csv_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ def _imp_df(column_names: list, importances: list) -> pd.DataFrame:
a dataframe of feature importances
"""
df = (
pd.DataFrame({"feature": column_names, "feature_importance": importances})
pd.DataFrame(
{"feature": column_names, "feature_importance": importances}
)
.sort_values("feature_importance", ascending=False)
.reset_index(drop=True)
)
Expand Down Expand Up @@ -193,7 +195,9 @@ def _perm_feat_imp(model, x, y):

def _parse_args(args) -> argparse.Namespace:
# parse command line
parser = argparse.ArgumentParser(description="Analyze an SSVC tree csv file")
parser = argparse.ArgumentParser(
description="Analyze an SSVC tree csv file"
)
parser.add_argument(
"csvfile", metavar="csvfile", type=str, help="the csv file to analyze"
)
Expand Down
9 changes: 7 additions & 2 deletions src/ssvc/decision_points/cvss/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@

from ssvc.decision_points.base import DecisionPointValue
from ssvc.decision_points.cvss._not_defined import NOT_DEFINED_X
from ssvc.decision_points.cvss.base import CvssDecisionPoint, CvssDecisionPoint as DecisionPoint
from ssvc.decision_points.cvss.base import (
CvssDecisionPoint,
CvssDecisionPoint as DecisionPoint,
)


def _modify_3(dp: DecisionPoint):
Expand Down Expand Up @@ -94,7 +97,9 @@ def _modify_4(dp: DecisionPoint):
for v in _dp_dict["values"]:
if v["key"] == "N":
v["name"] = "Negligible"
v["description"] = v["description"].replace(" no ", " negligible ")
v["description"] = v["description"].replace(
" no ", " negligible "
)
# we need to bump the version for this change
version = _dp_dict["version"]
ver = semver.Version.parse(version)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ def main():
from ssvc.decision_points.helpers import print_versions_and_diffs

print_versions_and_diffs(VERSIONS)
print_versions_and_diffs([
MSA_NoX,
])
print_versions_and_diffs(
[
MSA_NoX,
]
)


if __name__ == "__main__":
Expand Down
26 changes: 19 additions & 7 deletions src/ssvc/decision_points/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,33 +121,45 @@ def dp_diff(dp1: DecisionPoint, dp2: DecisionPoint) -> list[str]:
major = True

for name in dp2_names.difference(dp1_names):
diffs.append(f"(major or minor) {dp2.name} v{dp2.version} adds value {name}")
diffs.append(
f"(major or minor) {dp2.name} v{dp2.version} adds value {name}"
)
maybe_major = True
maybe_minor = True

# did the value keys change?
for name in intersection:
v1 = {value["name"]: value["key"] for value in dp1.model_dump()["values"]}
v1 = {
value["name"]: value["key"] for value in dp1.model_dump()["values"]
}
v1 = v1[name]

v2 = {value["name"]: value["key"] for value in dp2.model_dump()["values"]}
v2 = {
value["name"]: value["key"] for value in dp2.model_dump()["values"]
}
v2 = v2[name]

if v1 != v2:
diffs.append(f"(minor) {dp2.name} v{dp2.version} value {name} key changed")
diffs.append(
f"(minor) {dp2.name} v{dp2.version} value {name} key changed"
)
minor = True
else:
diffs.append(f"{dp2.name} v{dp2.version} value {name} key did not change")
diffs.append(
f"{dp2.name} v{dp2.version} value {name} key did not change"
)

# did the value descriptions change?
for name in intersection:
v1 = {
value["name"]: value["description"] for value in dp1.model_dump()["values"]
value["name"]: value["description"]
for value in dp1.model_dump()["values"]
}
v1 = v1[name]

v2 = {
value["name"]: value["description"] for value in dp2.model_dump()["values"]
value["name"]: value["description"]
for value in dp2.model_dump()["values"]
}
v2 = v2[name]

Expand Down
4 changes: 3 additions & 1 deletion src/ssvc/decision_points/ssvc/mission_impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@
description="Degradation of non-essential functions; chronic degradation would eventually harm essential functions",
)

MI_NONE = DecisionPointValue(name="None", key="N", description="Little to no impact")
MI_NONE = DecisionPointValue(
name="None", key="N", description="Little to no impact"
)

# combine MI_NONE and MI_NED into a single value
DEGRADED = DecisionPointValue(
Expand Down
Loading