Skip to content

Commit 2d3eefa

Browse files
committed
Merge branch 'main' into 705-model-national-cybersecurity-incident-scoring-system
# Conflicts: # data/schema/v1/Decision_Point-1-0-1.schema.json # src/ssvc/_mixins.py # src/ssvc/decision_points/base.py # src/ssvc/namespaces.py # src/test/test_mixins.py
2 parents bfa1d05 + 77baef3 commit 2d3eefa

File tree

9 files changed

+50
-15
lines changed

9 files changed

+50
-15
lines changed

.github/workflows/lint_md_changes.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- uses: actions/checkout@v4
1717
with:
1818
fetch-depth: 0
19-
- uses: tj-actions/changed-files@v45
19+
- uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32
2020
id: changed-files
2121
with:
2222
files: '**/*.md'

data/schema/v1/Decision_Point-1-0-1.schema.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@
4747
},
4848
"namespace": {
4949
"type": "string",
50-
"description": "Namespace (a short, unique string): For example, \"ssvc\" or \"cvss\" to indicate the source of the decision point. See SSVC Documentation for details.",
51-
"pattern": "^(?=.{3,25}$)(x_)?[a-z0-9]{3,4}([/.-]?[a-z0-9]+){0,22}$",
52-
"examples": ["ssvc", "cvss", "ssvc-jp", "ssvc/acme", "ssvc/example.com"]
50+
"description": "Namespace (a short, unique string): The value must be one of the official namespaces, currenlty \"ssvc\", \"cvss\" OR can start with 'x_' for private namespaces. See SSVC Documentation for details.",
51+
"pattern": "^(?=.{3,25}$)(x_)?[a-z0-9]{3}([/.-]?[a-z0-9]+){0,22}$",
52+
"examples": ["ssvc", "cvss", "x_custom","x_custom/extension"]
5353
},
5454
"version": {
5555
"type": "string",

docs/reference/code/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ These include:
66
- [CSV Analyzer](analyze_csv.md)
77
- [Policy Generator](policy_generator.md)
88
- [Outcomes](outcomes.md)
9+
- [Namespaces](namespaces.md)
910
- [Doctools](doctools.md)

docs/reference/code/namespaces.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SSVC Namespaces
2+
3+
::: ssvc.namespaces

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ nav:
120120
- CSV Analyzer: 'reference/code/analyze_csv.md'
121121
- Policy Generator: 'reference/code/policy_generator.md'
122122
- Outcomes: 'reference/code/outcomes.md'
123+
- Namespaces: 'reference/code/namespaces.md'
123124
- Doctools: 'reference/code/doctools.md'
124125
- Calculator: 'ssvc-calc/index.md'
125126
- About:

src/ssvc/_mixins.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class _Versioned(BaseModel):
3434

3535
@field_validator("version")
3636
@classmethod
37-
def validate_version(cls, value):
37+
def validate_version(cls, value: str) -> str:
3838
"""
3939
Validate the version field.
4040
Args:
@@ -61,7 +61,7 @@ class _Namespaced(BaseModel):
6161

6262
@field_validator("namespace", mode="before")
6363
@classmethod
64-
def validate_namespace(cls, value):
64+
def validate_namespace(cls, value: str) -> str:
6565
"""
6666
Validate the namespace field.
6767
The value will have already been checked against the pattern in the field definition.

src/ssvc/decision_points/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class SsvcDecisionPointValue(_Base, _Keyed, BaseModel):
6262
"""
6363

6464

65-
class SsvcDecisionPoint(_Valued, _Keyed, _Versioned, _Namespaced, _Base, BaseModel):
65+
class SsvcDecisionPoint(_Base, _Keyed, _Versioned, _Namespaced, BaseModel):
6666
"""
6767
Models a single decision point as a list of values.
6868
"""

src/ssvc/namespaces.py

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/usr/bin/env python
22
"""
3-
Provides a namespace enum
3+
SSVC objects use namespaces to distinguish between objects that arise from different
4+
stakeholders or analytical category sources. This module defines the official namespaces
5+
for SSVC and provides a method to validate namespace values.
46
"""
57
# Copyright (c) 2025 Carnegie Mellon University and Contributors.
68
# - see Contributors.md for a full list of Contributors
@@ -18,10 +20,8 @@
1820
import re
1921
from enum import StrEnum, auto
2022

21-
# extensions / experimental namespaces should start with the following prefix
22-
# this is to avoid conflicts with official namespaces
2323
X_PFX = "x_"
24-
24+
"""The prefix for extension namespaces. Extension namespaces must start with this prefix."""
2525

2626
# pattern to match
2727
# `(?=.{3,25}$)`: 3-25 characters long
@@ -32,12 +32,41 @@
3232
# `([/.-]?[a-z0-9]+){0,22}`: zero to 22 occurrences of the punctuation character followed by at least one alphanumeric character
3333
# (note that the total limit will kick in at or before this point)
3434
# `$`: end of the string
35-
NS_PATTERN = re.compile(r"^(?=.{3,25}$)(x_)?[a-z0-9]{3,4}([/.-]?[a-z0-9]+){0,22}$")
35+
NS_PATTERN = re.compile(r"^(?=.{3,25}$)(x_)?[a-z0-9]{3}([/.-]?[a-z0-9]+){0,22}$")
36+
"""The regular expression pattern for validating namespaces.
37+
38+
Note:
39+
Namespace values must
40+
41+
- be 3-25 characters long
42+
- contain only lowercase alphanumeric characters and limited punctuation characters (`/`,`.` and `-`)
43+
- have only one punctuation character in a row
44+
- start with 3-4 alphanumeric characters after the optional extension prefix
45+
- end with an alphanumeric character
46+
47+
See examples in the `NameSpace` enum.
48+
"""
3649

3750

3851
class NameSpace(StrEnum):
3952
"""
4053
Defines the official namespaces for SSVC.
54+
55+
The namespace value must be one of the members of this enum or start with the prefix specified in X_PFX.
56+
Namespaces must be 3-25 lowercase characters long and must start with 3-4 alphanumeric characters after the optional prefix.
57+
Limited punctuation characters (/.-) are allowed between alphanumeric characters, but only one at a time.
58+
59+
Example:
60+
Following are examples of valid and invalid namespace values:
61+
62+
- `ssvc` is *valid* because it is present in the enum
63+
- `custom` is *invalid* because it does not start with the experimental prefix and is not in the enum
64+
- `x_custom` is *valid* because it starts with the experimental prefix and meets the pattern requirements
65+
- `x_custom/extension` is *valid* because it starts with the experimental prefix and meets the pattern requirements
66+
- `x_custom/extension/with/multiple/segments` is *invalid* because it exceeds the maximum length
67+
- `x_custom//extension` is *invalid* because it has multiple punctuation characters in a row
68+
- `x_custom.extension.` is *invalid* because it does not end with an alphanumeric character
69+
- `x_custom.extension.9` is *valid* because it meets the pattern requirements
4170
"""
4271

4372
# auto() is used to automatically assign values to the members.
@@ -47,9 +76,10 @@ class NameSpace(StrEnum):
4776
NCISS = auto()
4877

4978
@classmethod
50-
def validate(cls, value):
79+
def validate(cls, value: str) -> str:
5180
"""
52-
Validate the namespace value.
81+
Validate the namespace value. Valid values are members of the enum or start with the experimental prefix and
82+
meet the specified pattern requirements.
5383
5484
Args:
5585
value: the namespace value to validate

src/test/test_mixins.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
from pydantic import BaseModel, ValidationError
1818

19-
from ssvc.namespaces import NameSpace
2019
from ssvc._mixins import _Base, _Keyed, _Namespaced, _Valued, _Versioned
20+
from ssvc.namespaces import NameSpace
2121

2222

2323
class TestMixins(unittest.TestCase):

0 commit comments

Comments
 (0)