diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 2ec251cec..6c73468c2 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -13,60 +13,81 @@ name: "CodeQL"
on:
push:
- branches: [ develop, main ]
+ branches: [ "develop", "main" ]
pull_request:
- # The branches below must be a subset of the branches above
- branches: [ develop ]
+ branches: [ "develop" ]
schedule:
- - cron: '44 7 * * 3'
+ - cron: '39 21 * * 4'
jobs:
analyze:
- name: Analyze
- runs-on: ubuntu-latest
+ name: Analyze (${{ matrix.language }})
+ # Runner size impacts CodeQL analysis time. To learn more, please see:
+ # - https://gh.io/recommended-hardware-resources-for-running-codeql
+ # - https://gh.io/supported-runners-and-hardware-resources
+ # - https://gh.io/using-larger-runners (GitHub.com only)
+ # Consider using larger runners or machines with greater resources for possible analysis time improvements.
+ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
+ timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
+ # required for all workflows
+ security-events: write
+
+ # required to fetch internal or private CodeQL packs
+ packages: read
+
+ # only required for workflows in private repositories
actions: read
contents: read
- security-events: write
strategy:
fail-fast: false
matrix:
- language: [ 'python' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
- # Learn more:
- # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
-
+ include:
+ - language: python
+ build-mode: none
+ # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
+ # Use `c-cpp` to analyze code written in C, C++ or both
+ # Use 'java-kotlin' to analyze code written in Java, Kotlin or both
+ # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
+ # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
+ # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
+ # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
+ # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- # moved from v1 to v2 20230301
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
+ build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
- # If this step fails, then you should remove it and run the build manually (see below)
- - name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
+ # queries: security-extended,security-and-quality
+ # If the analyze step fails for one of the languages you are analyzing with
+ # "We were unable to automatically build your code", modify the matrix above
+ # to set the build mode to "manual" for that language. Then modify this step
+ # to build your code.
# âšī¸ Command-line programs to run using the OS shell.
- # đ https://git.io/JvXDl
-
- # âī¸ If the Autobuild fails above, remove it and uncomment the following three lines
- # and modify them (or add more) to build your code if your project
- # uses a compiled language
-
- #- run: |
- # make bootstrap
- # make release
+ # đ See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
+ - if: matrix.build-mode == 'manual'
+ shell: bash
+ run: |
+ echo 'If you are using a "manual" build mode for one or more of the' \
+ 'languages you are analyzing, replace this with the commands to build' \
+ 'your code, for example:'
+ echo ' make bootstrap'
+ echo ' make release'
+ exit 1
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/conventional-pr.yml b/.github/workflows/conventional-pr.yml
index 6d7328584..1baf63492 100644
--- a/.github/workflows/conventional-pr.yml
+++ b/.github/workflows/conventional-pr.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Install dependencies
run: npm install @commitlint/cli @commitlint/config-conventional
diff --git a/.github/workflows/python-push.yml b/.github/workflows/python-push.yml
index 44353753e..85d2e7a5c 100644
--- a/.github/workflows/python-push.yml
+++ b/.github/workflows/python-push.yml
@@ -2,6 +2,8 @@
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: Trestle Deploy
+
+
on:
push:
branches:
@@ -20,20 +22,20 @@ jobs:
path: ~/Library/Caches/pip
- os: windows-latest
path: ~\AppData\Local\pip\Cache
- python-version: ['3.9', '3.10', '3.11']
+ python-version: ['${{ vars.PYTHON_MIN }}', '${{ vars.PYTHON_MAX }}']
steps:
- name: Don't mess with line endings
run: |
git config --global core.autocrlf false
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- - uses: actions/cache@v2
+ - uses: actions/cache@v4
with:
path: ${{ matrix.path }}
key: ${{ matrix.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('setup.cfg') }}
@@ -44,39 +46,39 @@ jobs:
run: |
make develop
- name: Setup pre-commit
- if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10') }}
+ if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == vars.PYTHON_MAX) }}
run: |
make pre-commit
- name: Install dependencies
run: |
make install
- name: Run md document formatting (mdformat)
- if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10') }}
+ if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == vars.PYTHON_MAX) }}
run: |
make mdformat
- name: Run code formatting (yapf)
- if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10') }}
+ if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == vars.PYTHON_MAX) }}
run: |
make code-format
- name: Run code linting (flake8)
- if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10') }}
+ if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == vars.PYTHON_MAX) }}
run: |
make code-lint
- name: Run code typing check (mypy)
- if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10') }}
+ if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == vars.PYTHON_MAX) }}
continue-on-error: true
run: |
make code-typing
- name: Validate website content (mkdocs)
- if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10') }}
+ if: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == vars.PYTHON_MAX) }}
run: |
make docs-validate
- name: Pytest Fast
- if: ${{ !(matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10') }}
+ if: ${{ !(matrix.os == 'ubuntu-latest' && matrix.python-version == vars.PYTHON_MAX) }}
run: |
make test
- name: Pytest Cov
- if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10' }}
+ if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == vars.PYTHON_MAX }}
run: |
make test-cov
deploy:
@@ -90,15 +92,15 @@ jobs:
url: https://pypi.org/p/compliance-trestle
if: github.ref == 'refs/heads/main' && github.repository == 'oscal-compass/compliance-trestle'
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
token: ${{ secrets.ADMIN_PAT }}
- - name: Set up Python 3.10
- uses: actions/setup-python@v2
+ - name: Set up Python ${{ vars.PYTHON_MAX }}
+ uses: actions/setup-python@v5
with:
- python-version: '3.10'
+ python-version: ${{ vars.PYTHON_MAX }}
- name: Install build tools
run: |
make develop
@@ -131,16 +133,16 @@ jobs:
# Temporary hack: allow develop as well as master to deploy docs.
if: github.ref == 'refs/heads/main'
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
token: ${{ secrets.ADMIN_PAT }}
- - name: Set up Python 3.10
- uses: actions/setup-python@v2
+ - name: Set up Python ${{ vars.PYTHON_MAX }}
+ uses: actions/setup-python@v5
# This is deliberately not using a custom credential as it relies on native github actions token to have push rights.
with:
- python-version: '3.10'
+ python-version: ${{ vars.PYTHON_MAX }}
- name: Install build tools
run: |
make develop
@@ -158,7 +160,7 @@ jobs:
cancel-in-progress: true
if: github.ref == 'refs/heads/main'
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
with:
submodules: true
ref: main
diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml
index 1952253ff..607bcb57b 100644
--- a/.github/workflows/python-test.yml
+++ b/.github/workflows/python-test.yml
@@ -13,19 +13,19 @@ jobs:
- name: Don't mess with line endings
run: |
git config --global core.autocrlf false
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
- python-version: '3.10'
- - uses: actions/cache@v2
+ python-version: ${{ vars.PYTHON_MAX }}
+ - uses: actions/cache@v4
with:
path: ~/.cache/pip
- key: ubuntu-latest-3.10-pip-${{ hashFiles('setup.cfg') }}
+ key: ubuntu-latest-${{ vars.PYTHON_MAX }}-pip-${{ hashFiles('setup.cfg') }}
restore-keys: |
- ubuntu-latest-3.10-pip-
+ ubuntu-latest-${{ vars.PYTHON_MAX }}-pip-
- name: Install build tools
run: |
make develop
@@ -66,19 +66,19 @@ jobs:
- name: Don't mess with line endings
run: |
git config --global core.autocrlf false
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
- python-version: '3.10'
- - uses: actions/cache@v2
+ python-version: ${{ vars.PYTHON_MAX }}
+ - uses: actions/cache@v4
with:
path: ~/.cache/pip
- key: ubuntu-latest-3.10-pip-${{ hashFiles('setup.cfg') }}
+ key: ubuntu-latest-${{ vars.PYTHON_MAX }}-pip-${{ hashFiles('setup.cfg') }}
restore-keys: |
- ubuntu-latest-3.10-pip-
+ ubuntu-latest-${{ vars.PYTHON_MAX }}-pip-
- name: Install build tools
run: |
make develop
@@ -92,7 +92,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
- python-version: ['3.9', '3.10', '3.11']
+ python-version: [ '${{ vars.PYTHON_MIN }}', '${{ vars.PYTHON_MAX }}' ]
include:
- os: ubuntu-latest
path: ~/.cache/pip
@@ -104,15 +104,15 @@ jobs:
- name: Don't mess with line endings
run: |
git config --global core.autocrlf false
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- - uses: actions/cache@v2
+ - uses: actions/cache@v4
with:
path: ${{ matrix.path }}
key: ${{ matrix.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('setup.cfg') }}
@@ -122,16 +122,16 @@ jobs:
run: |
make develop
- name: Pytest Fast
- if: ${{ !(matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10') }}
+ if: ${{ !(matrix.os == 'ubuntu-latest' && matrix.python-version == vars.PYTHON_MAX) }}
run: |
make test
- name: Pytest Cov
- if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10' }}
+ if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == vars.PYTHON_MAX }}
run: |
make test-cov
- name: Upload artifact
- if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10' }}
+ if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == vars.PYTHON_MAX }}
uses: actions/upload-artifact@v2
with:
name: coverage
@@ -148,19 +148,19 @@ jobs:
- name: Don't mess with line endings
run: |
git config --global core.autocrlf false
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Set up Python
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
- python-version: '3.10'
- - uses: actions/cache@v2
+ python-version: ${{ vars.PYTHON_MAX }}
+ - uses: actions/cache@v4
with:
path: ~/.cache/pip
- key: ubuntu-latest-3.10-pip-${{ hashFiles('setup.cfg') }}
+ key: ubuntu-latest-${{ vars.PYTHON_MAX }}-pip-${{ hashFiles('setup.cfg') }}
restore-keys: |
- ubuntu-latest-3.10-pip-
+ ubuntu-latest-${{ vars.PYTHON_MAX }}-pip-
- name: Install build tools
run: |
make develop
@@ -178,7 +178,7 @@ jobs:
-Dsonar.python.coverage.reportPaths=coverage.xml
-Dsonar.tests=tests/
-Dsonar.sources=trestle/
- -Dsonar.python.version='3.10'
+ -Dsonar.python.version=${{ vars.PYTHON_MAX }}
-Dsonar.projectKey=compliance-trestle
-Dsonar.organization=compliance-trestle
-Dsonar.cpd.exclusions=trestle/oscal/*.py
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
deleted file mode 100644
index 57a20dd8e..000000000
--- a/CODE_OF_CONDUCT.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# Contributor Covenant Code of Conduct
-
-## Our Pledge
-
-In the interest of fostering an open and welcoming environment, we as
-contributors and maintainers pledge to making participation in our project and
-our community a harassment-free experience for everyone, regardless of age, body
-size, disability, ethnicity, sex characteristics, gender identity and expression,
-level of experience, education, socio-economic status, nationality, personal
-appearance, race, religion, or sexual identity and orientation.
-
-## Our Standards
-
-Examples of behavior that contributes to creating a positive environment
-include:
-
-- Using welcoming and inclusive language
-- Being respectful of differing viewpoints and experiences
-- Gracefully accepting constructive criticism
-- Focusing on what is best for the community
-- Showing empathy towards other community members
-
-Examples of unacceptable behavior by participants include:
-
-- The use of sexualized language or imagery and unwelcome sexual attention or
- advances
-- Trolling, insulting/derogatory comments, and personal or political attacks
-- Public or private harassment
-- Publishing others' private information, such as a physical or electronic
- address, without explicit permission
-- Other conduct which could reasonably be considered inappropriate in a
- professional setting
-
-## Our Responsibilities
-
-Project maintainers are responsible for clarifying the standards of acceptable
-behavior and are expected to take appropriate and fair corrective action in
-response to any instances of unacceptable behavior.
-
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct, or to ban temporarily or
-permanently any contributor for other behaviors that they deem inappropriate,
-threatening, offensive, or harmful.
-
-## Scope
-
-This Code of Conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community. Examples of
-representing a project or community include using an official project e-mail
-address, posting via an official social media account, or acting as an appointed
-representative at an online or offline event. Representation of a project may be
-further defined and clarified by project maintainers.
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at avikas@in.ibm.com. All
-complaints will be reviewed and investigated and will result in a response that
-is deemed necessary and appropriate to the circumstances. The project team is
-obligated to maintain confidentiality with regard to the reporter of an incident.
-Further details of specific enforcement policies may be posted separately.
-
-Project maintainers who do not follow or enforce the Code of Conduct in good
-faith may face temporary or permanent repercussions as determined by other
-members of the project's leadership.
-
-## Attribution
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
-available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
-
-For answers to common questions about this code of conduct, see
-https://www.contributor-covenant.org/faq
-
-[homepage]: https://www.contributor-covenant.org
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index dba542d53..945075abf 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -210,3 +210,44 @@ pre-commit run flake8 --files trestle/*
```
Note that in both of these cases autogenerated files under `trestle/oscal` are excluded. Note that for IDE support `setup.cfg` maintains a cache of `flake8` configuration.
+
+## Developers Guide to trestle upgrade commensurate with OSCAL models upgrade
+
+This is a general guide on how to go about upgrading compliance-trestle to support a new version of OSCAL models.
+
+The steps are as follows:
+
+
+- Create in GitHub repo (remote)
+
- Clone upgrade branch into /trestle-upgrade folder (local <- remote)
+
- Download revised and new OSCAL models into folder /trestle-upgrade/release-schemas (local <- remote)
+
+
- Create & source python virtual environment venv.trestle-upgrade (local)
+
- Orient current folder to /trestle-upgrade (local)
+
- Run make develop (local)
+
- Make necessary code changes:
+
+- Run python scripts/gen_oscal.py (local)
+
- Run make test-all (local)
+
- Fix errors and failures via modification of code generation modules and existing trestle modules, as appropriate case-by-case (local)
+
- Repeat until all errors and failures are fixed (local)
+
+ - Run make code-format (local)
+
- Run make code-lint (local)
+
- Push /trestle-upgrade folder changes back to GitHub repo (local -> remote)
+
- Create PR for trestle-upgrade branch -> develop branch (remote)
+
- Get PR approval (remote)
+
- Merge PR into develop branch (remote)
+
- Create PR for develop branch -> main branch (remote)
+
- Create breaking change (remote)
+
- Get PR approval (remote)
+
- Merge develop branch into main branch (remote)
+
+
+______________________________________________________________________
+
+##### Overview of process to take OSCAL models and upgrade trestle Python code
+
+
diff --git a/README.md b/README.md
index f161fb06f..d00e401d5 100644
--- a/README.md
+++ b/README.md
@@ -29,12 +29,6 @@ Trestle provides tooling to help orchestrate the compliance process across a num
- Support within trestle to streamline management within a managed git environment.
- An underlying object model that supports developers interacting with OSCAL artifacts.
-## Important Note:
-
-The current version of trestle supports NIST OSCAL 1.1.2 as well as previous versions 1.1.x and 1.0.x. All files created by trestle will be output as OSCAL version 1.1.2.
-
-There was a breaking change in OSCAL moving from version 1.0.0 to 1.0.2 mainly due to `prop` becoming `props` in AssessmentResults. Those who require strict OSCAL 1.0.0 please use trestle version 0.37.x. That version is stable but will not have any features added, and we encourage all users to move to OSCAL 1.1.2. OSCAL version 1.0.0 files are still handled on import but any AssessmentResults must conform to the `props` in AssessmentResults OSCAL specification.
-
## Why Trestle
Compliance suffers from being a complex topic that is hard to articulate simply. It involves complete and accurate execution of multiple procedures across many disciplines (e.g. IT, HR, management) with periodic verification and audit of those procedures against controls.
@@ -92,7 +86,13 @@ A collection of demos utilizing trestle can be found in the related project [com
## Development status
-Compliance trestle is currently stable and is based on NIST OSCAL version 1.1.2, with active development continuing.
+### v3: stable (actively developed)
+
+- supports NIST OSCAL 1.1.2 as well as previous versions
+
+### v2: stable (maintenance mode)
+
+- supports NIST OSCAL 1.0.4 as well as previous versions
## Community meetings and communications
diff --git a/docs/contributing/github_actions_setup.md b/docs/contributing/github_actions_setup.md
new file mode 100644
index 000000000..8cf0bbb8f
--- /dev/null
+++ b/docs/contributing/github_actions_setup.md
@@ -0,0 +1,21 @@
+# Github actions setup
+
+Github actions contains variables which have opaque values to a user.
+The variables are documented here such that trestle can be setup on a fork etc.
+
+## Secrets
+
+- `ADMIN_PAT`: Github PAT with sufficient write access to merge content into `develop` and commit to `gh-pages` and `main`
+
+- `SONAR_TOKEN`: Token to sonarcloud with rights to the appropriate project.
+
+## Repository level variables
+
+- `PYTHON_MIN`: Minimum test version of python e.g. `3.9`
+- `PYTHON_MAX`: Maxmimum test version of python e.g. `3.11`
+
+## Authorization with pypi
+
+Pypi authorization must be setup following the procedure in the following documents
+
+- https://docs.pypi.org/trusted-publishers/adding-a-publisher/
diff --git a/docs/demonstrations-content.md b/docs/demonstrations-content.md
index d9c7dd095..c2059d9ce 100644
--- a/docs/demonstrations-content.md
+++ b/docs/demonstrations-content.md
@@ -1,50 +1,78 @@
# Trestle demonstration projects and content
-Trestle has a number of demonstrations setup in the [oscal-compass/compliance-trestle-demos](https://github.com/oscal-compass/compliance-trestle-demos)
-repository which is intended to be a single point of call for demonstrations and content.
+Trestle has a number of demonstrations setup in the
+[oscal-compass/compliance-trestle-demos](https://github.com/oscal-compass/compliance-trestle-demos)
+repository which is intended to be a single point of call for demonstrations and
+content.
-If you are interested in contributing a demonstration / content open a PR to the demonstration repo and a PR to
+If you are interested in contributing a demonstration / content open a PR to the
+demonstration repo and a PR to
[this page](https://github.com/oscal-compass/compliance-trestle/blob/develop/docs/demonstrations-content.md).
-Demonstrations, where practical, should include instructions on how they were created.
+Demonstrations, where practical, should include instructions on how they were
+created.
## Current demonstrations
## Simple sdk examples.
-[This folder](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/trestle_sdk_examples) contains a number of small examples for using the trestle OSCAL sdks.
-
-## Australian government Information Security Manual (ISM)
-
-This demonstration uses trestle as an SDK for generating OSCAL files. This demonstration downloads all currently available versions of the Australian Government ISM from [ACSC](https://www.acsc.gov.au) and converts those documents to a set of OSCAL catalogs and profiles. [Read more about the demo here](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/ISM_catalog_profile).
+[This folder](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/trestle_sdk_examples)
+contains a number of small examples for using the trestle OSCAL sdks.
## arc42 architectural template enforcement using trestle author.
-[arc42](https://arc42.org/) have created a set of open-source architecture documentation templates. This [demonstration](https://github.com/oscal-compass/compliance-trestle-arc42-demo)
+[arc42](https://arc42.org/) have created a set of open-source architecture
+documentation templates. This
+[demonstration](https://github.com/IBM/compliance-trestle-arc42-demo/tree/f1ed64cd65af3167a3b129585d758bf8a21a4a6c)
uses `trestle author` to enforce use of the (modified) arc42 templates.
-A CICD pipeline (using github actions) is used for this demonstration. The full repository, including working CICD is [here](https://github.com/oscal-compass/compliance-trestle-arc42-demo). Read more about the demo [here](https://github.com/oscal-compass/compliance-trestle-arc42-demo).
-
## Trestle flask microservice demonstration.
-`trestle` uses a python library called [pydantic](https://pydantic-docs.helpmanual.io/) to form the underlying OSCAL object models. [flask-pydantic](https://github.com/bauerji/flask_pydantic) introduces a mechanism which integrates pydantic models into flask, providing automated user input validation in one line of code. This demo accepts a catalog as a POSTed object, throwing errors if the catalog does not meet the schema, and returns the catalog in the response. Find the demonstration [here](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/trestle_flask_api).
+`trestle` uses a python library called
+[pydantic](https://pydantic-docs.helpmanual.io/) to form the underlying OSCAL
+object models. [flask-pydantic](https://github.com/bauerji/flask_pydantic)
+introduces a mechanism which integrates pydantic models into flask, providing
+automated user input validation in one line of code. This demo accepts a catalog
+as a POSTed object, throwing errors if the catalog does not meet the schema, and
+returns the catalog in the response. Find the demonstration
+[here](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/trestle_flask_api).
## Creating a CIS controls catalog from an excel spreadsheet.
-The Centre for Internet Security (CIS) produce a number of cross industry standards for IT security including their [platform specific benchmarks](https://www.cisecurity.org/cis-benchmarks/) and a suite of [controls](https://www.cisecurity.org/controls/). [This demo](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/CIS_controls) converts a spreadsheet of those controls into a a catalog and three profiles.
+The Centre for Internet Security (CIS) produce a number of cross industry
+standards for IT security including their
+[platform specific benchmarks](https://www.cisecurity.org/cis-benchmarks/) and a
+suite of [controls](https://www.cisecurity.org/controls/).
+[This demo](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/CIS_controls)
+converts a spreadsheet of those controls into a a catalog and three profiles.
## Creating an SSP using trestle author.
-`trestle author ssp-generate` and `trestle author ssp-assemble` allow users to generate first a set of markdown documents to allow easy editing of control responses and second to reassemble that information up into an OSCAL ssp document. [This is a 'baseline' demonstration](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/ssp_author_demo) with more sophisticated updates expected in the near term.
+`trestle author ssp-generate` and `trestle author ssp-assemble` allow users to
+generate first a set of markdown documents to allow easy editing of control
+responses and second to reassemble that information up into an OSCAL ssp
+document.
+[This is a 'baseline' demonstration](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/ssp_author_demo)
+with more sophisticated updates expected in the near term.
## Trestle Repository API (`trestle.core.repository`)
-`trestle.core.repository` is an API which abstracts users from the file system of a trestle repository. It provides a way for external developers to access a trestle repository without relying on presumptions (such as cwd being within the repository). Find the demo [here](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/trestle_repo_api_examples).
+`trestle.core.repository` is an API which abstracts users from the file system
+of a trestle repository. It provides a way for external developers to access a
+trestle repository without relying on presumptions (such as cwd being within the
+repository). Find the demo
+[here](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/trestle_repo_api_examples).
-## Converting a spreadsheet into a `component-definition`
+## Task examples
-Plenty of compliance content exists today in spreadsheets. This [demonstration](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/trestle_task_spread_sheet_to_component_definition) show how to use the xlsx-to-oscal-component-definition MVP functionality.
+### Convert a spreadsheet into a `component-definition`
-## Task examples
+This
+[demonstration](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/trestle_task_spread_sheet_to_component_definition)
+shows how to use the `trestle task xlsx-to-oscal-cd` functionality.
+
+### Convert a `xccdf` result into a partial `assessment-results`
-### Spreadsheet to component definition
+This
+[demonstration](https://github.com/oscal-compass/compliance-trestle-demos/tree/develop/trestle_task_xccdf_result_to_oscal_ar)
+shows how to use the `trestle task xccdf_result_to_oscal_ar` functionality.
diff --git a/docs/mkdocs_code_of_conduct.md b/docs/mkdocs_code_of_conduct.md
index 97b4f23de..d66c2b9b4 100644
--- a/docs/mkdocs_code_of_conduct.md
+++ b/docs/mkdocs_code_of_conduct.md
@@ -1 +1,5 @@
-{!CODE_OF_CONDUCT.md!}
+# Code of Conduct
+
+The code of conduct now resides at the organization level in the community repository. Please visit https://github.com/oscal-compass/community/blob/main/CODE_OF_CONDUCT.md to review the common code of conduct.
+
+Note: The code of conduct is subject to change as `compliance-trestle` is on-boarded as a CNFC sandbox project.
diff --git a/docs/tutorials/ssp_profile_catalog_authoring/ssp_profile_catalog_authoring.md b/docs/tutorials/ssp_profile_catalog_authoring/ssp_profile_catalog_authoring.md
index 43a3b343b..5679e173d 100644
--- a/docs/tutorials/ssp_profile_catalog_authoring/ssp_profile_catalog_authoring.md
+++ b/docs/tutorials/ssp_profile_catalog_authoring/ssp_profile_catalog_authoring.md
@@ -929,6 +929,24 @@ x-trestle-global:
title: comp prof aa
href: trestle://profiles/comp_prof/profile.json
sort-id: ac-01
+x-trestle-add-props:
+ # Add or modify control properties here
+ # Properties may be at the control or part level
+ # Add control level properties like this:
+ # - name: ac1_new_prop
+ # value: new property value
+ #
+ # Add properties to a statement part like this, where "b." is the label of the target statement part
+ # - name: ac1_new_prop
+ # value: new property value
+ # smt-part: b.
+ #
+ - name: prop_with_ns
+ value: prop with ns
+ ns: https://my_new_namespace
+ - name: prop_with_no_ns
+ value: prop with no ns
+ ns: https://my_added_namespace
---
# ac-1 - \[Access Control\] Policy and Procedures
@@ -1036,7 +1054,9 @@ lines or modify/remove the lines with `### ` in them, corresponding to system co
If you edit the control markdown files you may run `ssp-generate` again and your edits will not be overwritten. When writing out the markdown for a control, any existing markdown for that control will be read and the response text for each part will be re-inserted into the new markdown file. If the new markdown has added parts the original responses will be placed correctly in the new file, but if any part is removed from the source control json file then any corresponding prose will be lost after the next `ssp-generate`.
-## `trestle author ssp-assemble`
+The `x-trestle-add-props` key of the yaml header allows the addition of properties to the implemented requirements under the SSP control implementation, comprising `name`, `value`, and optionally a namespace `ns` value. Similarly to the `x-trestle-add-props` in profile authoring, the properties may be added at the control level, or attached to a top level statement part by adding a value, `smt-part` with value `a.`, `b.` or any other label for one of the top level statement parts.
+
+`trestle author ssp-assemble`
After manually edting the markdown and providing the responses for the control implementation requirements, the markdown can be assembled into a single json SSP file with:
@@ -1050,7 +1070,7 @@ As with all the `assemble` tools, you may optionally specify a `--name` for a co
If you do not specify component-defintions during assembly, the markdown should not refer to any components other than `This System`. Thus you may first generate markdown with `ssp-generate` and no component-definitions specified - and then you may assemble that ssp with `ssp-assemble` and no component-definitions specified - but only if there are no components other than `This System` referenced in the markdown. You may add new component implementation details to the markdown later, but any new components must be defined in a component-defintion file, and that file must be specified when `ssp-assemble` is run.
-## Inheritance view
+`ssp inheritance view`
The inheritance view is generated by setting the `--leveraged-ssp` flag with `trestle author ssp-generate`. It contains information relating to exported information such as inherited capabilities and customer responsibilities that can be used to populate the inheritance information in the assembled SSP. When used, a directory named "inheritance" is created within the markdown directory. This directory serves as a designated space for mapping inherited capabilities and responsibilities onto components in the assemble SSP and authoring satisfied statements for responsibilities.
diff --git a/images/trestle-OSCAL-upgrade.drawio b/images/trestle-OSCAL-upgrade.drawio
new file mode 100644
index 000000000..3f7c5ac67
--- /dev/null
+++ b/images/trestle-OSCAL-upgrade.drawio
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/images/trestle-OSCAL-upgrade.png b/images/trestle-OSCAL-upgrade.png
new file mode 100644
index 000000000..eae3bb6ef
Binary files /dev/null and b/images/trestle-OSCAL-upgrade.png differ
diff --git a/setup.cfg b/setup.cfg
index 3c57303b5..0b3f35c1c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -34,15 +34,15 @@ install_requires =
furl
pydantic[email]>=2.0.0
python-dotenv>=0.10.4
- datamodel-code-generator[http] >= 0.11.14
+ datamodel-code-generator[http] == 0.25.3
python-frontmatter
pywin32 >= 1.0;platform_system=='Windows'
defusedxml
openpyxl~=3.0
- Jinja2 == 3.1.3
+ Jinja2 == 3.1.4
cmarkgfm==2024.1.* #Update regularly
orjson
- requests
+ requests>=2.32.2
importlib_resources
[options.packages.find]
@@ -66,7 +66,7 @@ dev =
pytest-xdist
pre-commit>=2.4.0
setuptools
- urllib3==1.26.17
+ urllib3==1.26.19
wheel
yapf
python-semantic-release>=9.8.0
@@ -105,7 +105,7 @@ max-line-length=120
exclude = trestle/oscal
[mypy]
-plugins = pydantic.mypy
+plugins = pydantic.v1.mypy
ignore_missing_imports = True
strict_optional = True
diff --git a/tests/trestle/core/commands/author/ssp_test.py b/tests/trestle/core/commands/author/ssp_test.py
index 8f9eaf550..b2afbfc9c 100644
--- a/tests/trestle/core/commands/author/ssp_test.py
+++ b/tests/trestle/core/commands/author/ssp_test.py
@@ -15,6 +15,7 @@
import argparse
import pathlib
+from typing import Dict, List
from _pytest.monkeypatch import MonkeyPatch
@@ -1113,3 +1114,57 @@ def test_ssp_gen_throw_exception_for_rep_comps(tmp_trestle_dir: pathlib.Path, mo
# first create the markdown
ssp_gen = SSPGenerate()
assert ssp_gen._run(gen_args) == 1
+
+
+def test_ssp_gen_and_assemble_add_props(tmp_trestle_dir: pathlib.Path) -> None:
+ """Test ssp generate and assemble with additional properties processing."""
+ gen_args, _ = setup_for_ssp(tmp_trestle_dir, prof_name, ssp_name)
+ gen_args.yaml_header = None
+ ssp_cmd = SSPGenerate()
+
+ assert ssp_cmd._run(gen_args) == 0
+
+ md_path = tmp_trestle_dir / ssp_name / 'ac' / 'ac-1.md'
+ assert md_path.exists()
+
+ md_api = MarkdownAPI()
+ header, tree = md_api.processor.process_markdown(md_path)
+
+ # Create key in header for add props for now
+ ac_1_properties: Dict[str, str] = {
+ 'name': 'prop_with_ns', 'value': 'prop with ns', 'ns': 'https://my_new_namespace'
+ }
+ ac_1_smt_properties: Dict[str, str] = {'name': 'smt_prop', 'value': 'smt prop', 'smt-part': 'a.'}
+ # Verify the add props header value is present
+ properties: List[Dict[str, str]] = header.get('x-trestle-add-props')
+ properties.extend([ac_1_properties, ac_1_smt_properties])
+
+ md_api.write_markdown_with_header(md_path, header, tree.content.raw_text)
+
+ args_compdefs = gen_args.compdefs
+ # now assemble controls into json ssp
+ ssp_assemble = SSPAssemble()
+ args = argparse.Namespace(
+ trestle_root=tmp_trestle_dir,
+ markdown=ssp_name,
+ output=ssp_name,
+ verbose=1,
+ name=None,
+ version=None,
+ compdefs=args_compdefs,
+ regenerate=False,
+ )
+ assert ssp_assemble._run(args) == 0
+
+ assem_ssp, _ = ModelUtils.load_model_for_class(tmp_trestle_dir, ssp_name, ossp.SystemSecurityPlan)
+ impl_reqs = assem_ssp.control_implementation.implemented_requirements
+ impl_req = next((i_req for i_req in impl_reqs if i_req.control_id == 'ac-1'), None)
+ assert len(impl_req.props) == 1
+ assert impl_req.props[0].name == 'prop_with_ns'
+ assert impl_req.props[0].value == 'prop with ns'
+ assert impl_req.props[0].ns == 'https://my_new_namespace'
+
+ smt_a = next((smt for smt in impl_req.statements if smt.statement_id == 'ac-1_smt.a'), None)
+ assert len(smt_a.props) == 1
+ assert smt_a.props[0].name == 'smt_prop'
+ assert smt_a.props[0].value == 'smt prop'
diff --git a/tests/trestle/core/commands/author/versioning/template_versioning_test.py b/tests/trestle/core/commands/author/versioning/template_versioning_test.py
index 8d7fbb105..10f04bb26 100644
--- a/tests/trestle/core/commands/author/versioning/template_versioning_test.py
+++ b/tests/trestle/core/commands/author/versioning/template_versioning_test.py
@@ -222,6 +222,7 @@ def test_valid_version() -> None:
assert not TemplateVersioning.is_valid_version('0.1')
assert not TemplateVersioning.is_valid_version('1')
assert not TemplateVersioning.is_valid_version('0.0.0.1')
+ assert not TemplateVersioning.is_valid_version('0a0b1')
def test_empty_folder_is_not_created(tmp_path: pathlib.Path) -> None:
diff --git a/tests/trestle/misc/_inventory.py b/tests/trestle/misc/_inventory.py
new file mode 100644
index 000000000..830c2151f
--- /dev/null
+++ b/tests/trestle/misc/_inventory.py
@@ -0,0 +1,23 @@
+# test.py
+from uuid import uuid4
+
+from trestle.oscal.common import InventoryItem, ResponsibleParty
+
+try:
+ role_id = 'x'
+ party_uuids = [str(uuid4())]
+ rp = ResponsibleParty(
+ role_id=role_id,
+ party_uuids=party_uuids,
+ )
+ list_rp = [rp]
+ item = InventoryItem(
+ uuid=str(uuid4()),
+ description='an item',
+ props=[],
+ links=[],
+ responsible_parties=list_rp,
+ implemented_components=[],
+ )
+except Exception as e:
+ raise RuntimeError(f'{e}')
diff --git a/tests/trestle/misc/mypy.cfg b/tests/trestle/misc/mypy.cfg
new file mode 100644
index 000000000..67e28286b
--- /dev/null
+++ b/tests/trestle/misc/mypy.cfg
@@ -0,0 +1,27 @@
+
+# copied from setup.cfg
+
+[mypy]
+plugins = pydantic.v1.mypy
+
+ignore_missing_imports = True
+strict_optional = True
+warn_redundant_casts = True
+warn_unused_ignores = True
+disallow_any_generics = True
+check_untyped_defs = True
+no_implicit_reexport = True
+show_error_codes = True
+show_error_context = True
+# disallow-untyped-calls = True
+disallow_untyped_defs = True
+disable_error_code = union-attr, attr-defined, no-redef, assignment, arg-type, list-item
+
+[mypy-trestle.oscal.*]
+ignore_errors = True
+
+[pydantic-mypy]
+init_forbid_extra = True
+init_typed = True
+warn_required_dynamic_aliases = True
+warn_untyped_fields = True
\ No newline at end of file
diff --git a/tests/trestle/misc/mypy_test.py b/tests/trestle/misc/mypy_test.py
new file mode 100644
index 000000000..5634b0fe3
--- /dev/null
+++ b/tests/trestle/misc/mypy_test.py
@@ -0,0 +1,38 @@
+# -*- mode:python; coding:utf-8 -*-
+# Copyright (c) 2024 IBM Corp. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Testing mypy."""
+
+import pathlib
+import subprocess
+
+folder = pathlib.Path('tests') / 'trestle' / 'misc'
+subject = folder / '_inventory.py'
+config = folder / 'mypy.cfg'
+
+
+def test_mypy(tmp_path) -> None:
+ """Testing mypy."""
+ cmd = ['trestle', 'version']
+ process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ output_bytes, _ = process.communicate()
+ output = output_bytes.decode('utf-8')
+ assert 'Trestle version' in output
+ assert 'based on OSCAL version' in output
+ #
+ cmd = ['mypy', '--config-file', f'{config}', f'{subject}']
+ process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ output_bytes, _ = process.communicate()
+ output = output_bytes.decode('utf-8')
+ assert 'Success: no issues found in 1 source file' in output
diff --git a/tests/trestle/tasks/csv_to_oscal_cd_test.py b/tests/trestle/tasks/csv_to_oscal_cd_test.py
index e2ebc1fa3..3fd7d2800 100644
--- a/tests/trestle/tasks/csv_to_oscal_cd_test.py
+++ b/tests/trestle/tasks/csv_to_oscal_cd_test.py
@@ -23,6 +23,8 @@
from _pytest.monkeypatch import MonkeyPatch
+import pytest
+
from tests import test_utils
import trestle.tasks.csv_to_oscal_cd as csv_to_oscal_cd
@@ -1419,6 +1421,28 @@ def test_execute_validation(tmp_path: pathlib.Path) -> None:
assert len(component.control_implementations) == 0
+def test_execute_invalid_set_parameter_values(tmp_path: pathlib.Path) -> None:
+ """Test execute invalid set parameter (PARAMETER_ID, Parameter_Value_Default) values."""
+ _, section = _get_config_section_init(tmp_path, 'test-csv-to-oscal-cd-bp.config')
+ # inject invalid set parameter values
+ rows = _get_rows('tests/data/csv/bp.sample.v2.csv')
+ assert rows[3][13] == 'allowed_admins_per_account'
+ assert rows[3][15] == '10'
+ rows[3][13] = 'allowed admins per account'
+ rows[3][15] = '10 '
+ with mock.patch('trestle.tasks.csv_to_oscal_cd.csv.reader') as mock_csv_reader:
+ mock_csv_reader.return_value = rows
+ tgt = csv_to_oscal_cd.CsvToOscalComponentDefinition(section)
+ with pytest.raises(RuntimeError) as exc_info:
+ tgt._execute()
+
+ exeception_value = str(exc_info.value)
+ assert exeception_value == (
+ 'row 4: "allowed admins per account" is invalid for column Parameter_Id '
+ 'and/or "10 " is invalid for column Parameter_Value_Default'
+ )
+
+
def test_row_property_builder(tmp_path):
"""Test row property builder."""
# valid
diff --git a/trestle/core/catalog/catalog_reader.py b/trestle/core/catalog/catalog_reader.py
index ed118d84d..f887ea191 100644
--- a/trestle/core/catalog/catalog_reader.py
+++ b/trestle/core/catalog/catalog_reader.py
@@ -322,11 +322,37 @@ def _add_set_params_to_item(param_dict: Dict[str, str], item: TypeWithSetParams,
item.set_parameters = new_sp_list
item.set_parameters.append(ossp.SetParameter(param_id=param_id, values=param_values))
+ @staticmethod
+ def _add_props_to_imp_req(
+ control_id: str,
+ part_id_map_by_label: Dict[str, Dict[str, str]],
+ yaml_header: Dict[str, Any],
+ imp_req: ossp.ImplementedRequirement
+ ) -> None:
+ """Add the props from the yaml header to the imp_req."""
+ control_part_id_map = part_id_map_by_label.get(control_id, {})
+ props, props_by_id = ControlReader.get_props_list(control_id, control_part_id_map, yaml_header)
+ # add the props at control level
+ if props:
+ imp_req.props = as_list(imp_req.props)
+ imp_req.props.extend(props)
+
+ # add the props at the part level
+ for label, part_id in control_part_id_map.items():
+ props = props_by_id.get(label, [])
+ if not props:
+ continue
+ for statement in as_list(imp_req.statements):
+ if statement.statement_id == part_id:
+ statement.props = as_list(statement.props)
+ statement.props.extend(props)
+
@staticmethod
def _update_ssp_with_md_header(
ssp: ossp.SystemSecurityPlan,
control_id: str,
comp_dict: Dict[str, generic.GenericComponent],
+ part_label_to_id_map: Dict[str, Dict[str, str]],
md_header: Dict[str, Dict[str, str]]
) -> None:
"""Update the ssp with info from the header of an ssp control markdown file."""
@@ -346,6 +372,8 @@ def _update_ssp_with_md_header(
if const.SSP_VALUES in param_dict:
CatalogReader._add_set_params_to_item(param_dict, imp_req, param_id)
+ CatalogReader._add_props_to_imp_req(control_id, part_label_to_id_map, md_header, imp_req)
+
@staticmethod
def read_ssp_md_content(
md_path: pathlib.Path,
@@ -398,4 +426,4 @@ def read_ssp_md_content(
CatalogReader._update_ssp_with_comp_info(
ssp, control_id, comp_dict[comp_name], comp_info_dict, part_id_map_by_label
)
- CatalogReader._update_ssp_with_md_header(ssp, control_id, comp_dict, md_header)
+ CatalogReader._update_ssp_with_md_header(ssp, control_id, comp_dict, part_id_map_by_label, md_header)
diff --git a/trestle/core/commands/author/versioning/template_versioning.py b/trestle/core/commands/author/versioning/template_versioning.py
index e9c954d87..43f524830 100644
--- a/trestle/core/commands/author/versioning/template_versioning.py
+++ b/trestle/core/commands/author/versioning/template_versioning.py
@@ -212,7 +212,7 @@ def is_valid_version(template_version: str) -> bool:
return True # we can have empty version
if template_version == '0.0.0':
return False
- version_regex = r'^[0-9]+.[0-9]+.[0-9]+$'
+ version_regex = r'^[0-9]+\.[0-9]+\.[0-9]+$'
pattern = re.compile(version_regex)
if pattern.search(template_version):
return True
diff --git a/trestle/core/control_reader.py b/trestle/core/control_reader.py
index 571ad480f..c1881c488 100644
--- a/trestle/core/control_reader.py
+++ b/trestle/core/control_reader.py
@@ -325,8 +325,8 @@ def read_implemented_requirement(control_file: pathlib.Path,
return sort_id, imp_req
@staticmethod
- def _get_props_list(control_id: str, label_map: Dict[str, str],
- yaml_header: Dict[str, Any]) -> Tuple[List[common.Property], Dict[str, List[common.Property]]]:
+ def get_props_list(control_id: str, label_map: Dict[str, str],
+ yaml_header: Dict[str, Any]) -> Tuple[List[common.Property], Dict[str, List[common.Property]]]:
"""Get the list of props in the yaml header of this control as separate lists with and without by_id."""
prop_list = yaml_header.get(const.TRESTLE_ADD_PROPS_TAG, [])
props = []
@@ -388,7 +388,7 @@ def read_editable_content(
if header_params:
param_dict.update(header_params)
- props, props_by_id = ControlReader._get_props_list(control_id, part_label_to_id_map, yaml_header)
+ props, props_by_id = ControlReader.get_props_list(control_id, part_label_to_id_map, yaml_header)
# When adding props without by_id it can either be starting or ending and we default to ending
# This is the default behavior as described for implicit binding in
diff --git a/trestle/core/control_writer.py b/trestle/core/control_writer.py
index a6e95cb7d..8d9fbae3c 100644
--- a/trestle/core/control_writer.py
+++ b/trestle/core/control_writer.py
@@ -487,6 +487,9 @@ def write_control_for_editing(
elif context.purpose == ContextPurpose.SSP:
header_comment_dict[const.SET_PARAMS_TAG] = const.YAML_SSP_VALUES_COMMENT
header_comment_dict[const.COMP_DEF_RULES_PARAM_VALS_TAG] = const.YAML_RULE_PARAM_VALUES_SSP_COMMENT
+ # The add props information to SSP only. It is added to the profile later.
+ if const.TRESTLE_ADD_PROPS_TAG not in md_header:
+ md_header[const.TRESTLE_ADD_PROPS_TAG] = []
elif context.purpose == ContextPurpose.COMPONENT:
header_comment_dict[const.COMP_DEF_RULES_PARAM_VALS_TAG
] = const.YAML_RULE_PARAM_VALUES_COMPONENT_COMMENT
diff --git a/trestle/tasks/csv_to_oscal_cd.py b/trestle/tasks/csv_to_oscal_cd.py
index 367359ca3..a7343e02d 100644
--- a/trestle/tasks/csv_to_oscal_cd.py
+++ b/trestle/tasks/csv_to_oscal_cd.py
@@ -673,12 +673,20 @@ def _create_set_parameters(self, rule_key: tuple) -> List[SetParameter]:
name = self._csv_mgr.get_value(rule_key, parameter_id_column_name)
value = self._csv_mgr.get_value(rule_key, parameter_value_default_column_name)
if name and value:
- values = self._str_to_list(value)
- set_parameter = SetParameter(
- param_id=name,
- values=values,
- )
- set_parameters.append(set_parameter)
+ try:
+ values = self._str_to_list(value)
+ set_parameter = SetParameter(
+ param_id=name,
+ values=values,
+ )
+ set_parameters.append(set_parameter)
+ except Exception:
+ row_number = self._csv_mgr.get_row_number(rule_key)
+ text = (
+ f'row {row_number}: "{name}" is invalid for column {parameter_id_column_name} '
+ f'and/or "{",".join(values)}" is invalid for column {parameter_value_default_column_name}'
+ )
+ raise RuntimeError(text)
elif name:
row_number = self._csv_mgr.get_row_number(rule_key)
text = f'row "{row_number}" missing value for "{parameter_value_default_column_name}"'