├── .docker
└── config.json
├── .github
├── dependabot.yml
└── workflows
│ ├── codeql-analysis.yml
│ ├── deploy.yml
│ ├── integration-dev.yml
│ └── integration.yml
├── .gitignore
├── .semgrepignore
├── CHANGELOG.md
├── LICENSE
├── MANIFEST.in
├── README.md
├── docs
├── examples
│ ├── U_Docker_Enterprise_2-x_Linux-UNIX_V1R1_STIG.zip
│ └── docker.ckl
├── images
│ └── STIG_Parser.png
└── mapping.md
├── requirements.txt
├── setup.py
├── src
└── stig_parser
│ ├── __init__.py
│ ├── stig_parser.py
│ ├── version.py
│ └── xccdf-1.1.4.xsd
└── tests
├── __init__.py
├── resources
└── U_Docker_Enterprise_2-x_Linux-UNIX_V1R1_STIG.zip
└── stig_parser_test.py
/.docker/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "image": "python:3.10.4-bullseye"
3 | }
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | # Maintain dependencies for GitHub Actions
4 | - package-ecosystem: "github-actions"
5 | directory: "/"
6 | schedule:
7 | interval: "weekly"
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master, development ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '16 22 * * 2'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'python' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
37 | # Learn more:
38 | # 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
39 |
40 | steps:
41 | - name: Checkout repository
42 | uses: actions/checkout@v3
43 |
44 | # Initializes the CodeQL tools for scanning.
45 | - name: Initialize CodeQL
46 | uses: github/codeql-action/init@v2
47 | with:
48 | languages: ${{ matrix.language }}
49 | # If you wish to specify custom queries, you can do so here or in a config file.
50 | # By default, queries listed here will override any specified in a config file.
51 | # Prefix the list here with "+" to use these queries and those in the config file.
52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
53 |
54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
55 | # If this step fails, then you should remove it and run the build manually (see below)
56 | - name: Autobuild
57 | uses: github/codeql-action/autobuild@v2
58 |
59 | # ℹ️ Command-line programs to run using the OS shell.
60 | # 📚 https://git.io/JvXDl
61 |
62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
63 | # and modify them (or add more) to build your code if your project
64 | # uses a compiled language
65 |
66 | #- run: |
67 | # make bootstrap
68 | # make release
69 |
70 | - name: Perform CodeQL Analysis
71 | uses: github/codeql-action/analyze@v2
72 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | ## NAME GITHUB ACTION WORKFLOW
2 | name: Build & Deploy PyPi Package
3 |
4 | ## RUN WORKFLOW WHEN
5 | on:
6 | ## CREATION OF NEW RELEASE
7 | release:
8 | types: [published]
9 |
10 | ## DEFINE WORKFLOW
11 | jobs:
12 | ## DEFINE BUILD JOB
13 | build-publish:
14 | ## DEFINE NAME
15 | name: "Build & Publish"
16 |
17 | ## DEFINE WORKER
18 | runs-on: ubuntu-latest
19 |
20 | ## DEFINE STEPS
21 | steps:
22 | ## CLONE REPO CODE
23 | - name: "Checkout Source Code"
24 | uses: actions/checkout@v3
25 |
26 | ## SETUP PYTHON3
27 | - name: "Configure Runner Python"
28 | uses: actions/setup-python@v3
29 | with:
30 | python-version: 3.8
31 |
32 | ## DEBUG
33 | - name: "Debug Print Step"
34 | run: |
35 | ls -al
36 | pwd
37 |
38 | ## CONFIGURE WORKER TO PUBLISH PACKAGE
39 | - name: "Install Dependencies"
40 | run: |
41 | # Upgrade pip
42 | python3 -m pip install --upgrade pip
43 | # Install Dependencies
44 | python3 -m pip install --upgrade build xmltodict
45 |
46 | ## BUILD PACKAGE
47 | - name: "Build Python Package"
48 | run: |
49 | # Build Python Package
50 | python3 -m build
51 |
52 | ## PUBLISH PACKAGE TO PYPI
53 | - name: "Publish Package to PyPi"
54 | uses: pypa/gh-action-pypi-publish@master
55 | with:
56 | password: ${{ secrets.PYPI_TOKEN }}
57 | #repository_url: https://test.pypi.org/legacy/
58 |
--------------------------------------------------------------------------------
/.github/workflows/integration-dev.yml:
--------------------------------------------------------------------------------
1 | ## NAME GITHUB ACTION WORKFLOW
2 | name: STIG Parser Integration (DEV)
3 |
4 | ## RUN WORKFLOW WHEN
5 | on:
6 | ## PULL REQUEST TO DEV
7 | pull_request:
8 | branches: [ development ]
9 |
10 | ## DEFINE WORKFLOW
11 | jobs:
12 | ## DEFINE LINT TEST JOB
13 | linting:
14 | ## DEFINE JOB NAME
15 | name: "LINTING"
16 |
17 | ## DEFINE WORKER
18 | runs-on: ubuntu-latest
19 | strategy:
20 | matrix:
21 | python-version: [3.8, 3.9]
22 |
23 | ## DEFINE STEPS
24 | steps:
25 | ## CLONE REPO CODE
26 | - uses: actions/checkout@v3
27 |
28 | ## SETUP PYTHON3
29 | - name: Set up Python ${{ matrix.python-version }}
30 | uses: actions/setup-python@v3
31 | with:
32 | python-version: ${{ matrix.python-version }}
33 |
34 | ## INSTALL DEPENDENCIES ON WORKER
35 | - name: Install Dependencies
36 | run: |
37 | python -m pip install --upgrade pip
38 | pip install flake8
39 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
40 |
41 | ## PERFORM LINT WITH FLAKE8
42 | - name: Lint with flake8
43 | run: |
44 | # stop the build if there are Python syntax errors or undefined names
45 | flake8 ./src --count --select=E9,F63,F7,F82 --show-source --statistics
46 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
47 | flake8 ./src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
48 |
49 | ## DEFINE UNIT TESTING JOB
50 | testing:
51 | ## DEFINE JOB NAME
52 | name: "TESTING"
53 |
54 | ## WAIT FOR PREVIOUS JOB
55 | needs: linting
56 |
57 | ## DEFINE WORKER
58 | runs-on: ubuntu-latest
59 | strategy:
60 | matrix:
61 | python-version: [3.8, 3.9]
62 |
63 | ## DEFINE STEPS
64 | steps:
65 | ## CLONE REPO CODE
66 | - uses: actions/checkout@v3
67 |
68 | ## SETUP PYTHON3
69 | - name: Set up Python ${{ matrix.python-version }}
70 | uses: actions/setup-python@v3
71 | with:
72 | python-version: ${{ matrix.python-version }}
73 |
74 | ## INSTALL DEPENDENCIES ON WORKER
75 | - name: Install Dependencies
76 | run: |
77 | python -m pip install --upgrade pip
78 | pip install pytest pytest-cov
79 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
80 |
81 | ## PERFORM UNIT TEST WITH PYTEST
82 | - name: Test with pytest
83 | run: |
84 | pytest -v --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html
85 |
86 | ## DEFINE CODE COVERAGE JOB
87 | code-cov:
88 | ## DEFINE NAME:
89 | name: "CODE COVERAGE"
90 |
91 | ## WAIT FOR PREVIOUS JOB
92 | needs: testing
93 |
94 | ## DEFINE WORKER
95 | runs-on: ubuntu-latest
96 |
97 | ## DEFINE ENVIRONMENT VARIABLES
98 | env:
99 | COVERAGE_SINGLE: 60 ## MINIMUM COVERAGE PERCENTAGE PER FILE
100 | COVERAGE_TOTAL: 60 ## MINIMUM COVERAGE PERCENTAGE TOTAL
101 |
102 | ## DEFINE STEPS
103 | steps:
104 | ## CLONE REPO CODE
105 | - uses: actions/checkout@v3
106 |
107 | ## SETUP PYTHON
108 | - name: Set up Python 3.9
109 | uses: actions/setup-python@v3
110 | with:
111 | python-version: 3.9
112 |
113 | ## INSTALL DEPENDENCIES
114 | - name: Install dependencies
115 | run: |
116 | python -m pip install --upgrade pip
117 | pip install flake8 pytest xmltodict
118 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
119 |
120 | ## PERFORM CODE COVERAGE TEST
121 | - name: "PyTest & CodeCov"
122 | id: pytest
123 | uses: programmingwithalex/pytester-cov@v1.2.4
124 | with:
125 | #pytest-root-dir: './src/'
126 | cov-omit-list: 'test/*, dev/*, docs/*, src/version.py'
127 | cov-threshold-single: ${{ env.COVERAGE_SINGLE }}
128 | cov-threshold-total: ${{ env.COVERAGE_TOTAL }}
129 |
130 | ## ADD RESULTS TO COMMIT
131 | #- name: Commit pytest coverage table
132 | # uses: peter-evans/commit-comment@v1
133 | # with:
134 | # body: ${{ steps.pytest.outputs.output-table }}
135 |
136 | ## SEND NOTIFICATION TO SLACK
137 | #- name: Slack Notification
138 | # uses: rtCamp/action-slack-notify@v2
139 | # env:
140 | # SLACK_TITLE: 'CodeCov Test Results'
141 | # SLACK_MESSAGE: ${{ steps.pytest.outputs.output-table }}
142 | # SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
143 |
144 | ## DEFINE SECURITY JOB
145 | security:
146 | ## DEFINE NAME
147 | name: "SECURITY"
148 |
149 | ## WAIT FOR PREVIOUS JOB
150 | needs: code-cov
151 |
152 | ## DEFINE WORKER
153 | runs-on: ubuntu-latest
154 |
155 | ## SKIP IF PR WAS CREATED BY DEPENDABOT (PERMISSIONS ISSUE)
156 | if: (github.actor != 'dependabot[bot]')
157 |
158 | ## DEFINE STEPS
159 | steps:
160 | ## CLONE REPO CODE
161 | - uses: actions/checkout@v3
162 |
163 | ## CACHE VULNERABILITY DATABASE
164 | - name: Cache multiple paths
165 | uses: actions/cache@v3
166 | with:
167 | path: |
168 | ${{ github.workspace }}/db
169 | key: ${{ runner.os }}-${{ hashFiles('requirements.txt') }}
170 |
171 | ## PERFORM SECURITY SCANS
172 | - name: Perform Scan
173 | uses: ShiftLeftSecurity/scan-action@master
174 | env:
175 | VDB_HOME: ${{ github.workspace }}/db
176 | WORKSPACE: ""
177 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
178 | with:
179 | output: reports
180 |
181 | ## UPLOAD RESULTS TO GITHUB SECURITY TAB
182 | - name: Upload SARIF file for GitHub Advanced Security Dashboard
183 | uses: github/codeql-action/upload-sarif@v2
184 | with:
185 | sarif_file: reports
--------------------------------------------------------------------------------
/.github/workflows/integration.yml:
--------------------------------------------------------------------------------
1 | ## NAME GITHUB ACTION WORKFLOW
2 | name: STIG Parser Integration
3 |
4 | ## RUN WORKFLOW WHEN
5 | on:
6 | ## PULL REQUEST TO MASTER
7 | pull_request:
8 | branches: [ master ]
9 |
10 | ## DEFINE WORKFLOW
11 | jobs:
12 | ## DEFINE LINT TEST JOB
13 | linting:
14 | ## DEFINE JOB NAME
15 | name: "LINTING"
16 |
17 | ## DEFINE WORKER
18 | runs-on: ubuntu-latest
19 | strategy:
20 | matrix:
21 | python-version: [3.8, 3.9]
22 |
23 | ## DEFINE STEPS
24 | steps:
25 | ## CLONE REPO CODE
26 | - uses: actions/checkout@v3
27 |
28 | ## SETUP PYTHON3
29 | - name: Set up Python ${{ matrix.python-version }}
30 | uses: actions/setup-python@v3
31 | with:
32 | python-version: ${{ matrix.python-version }}
33 |
34 | ## INSTALL DEPENDENCIES ON WORKER
35 | - name: Install Dependencies
36 | run: |
37 | python -m pip install --upgrade pip
38 | pip install flake8
39 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
40 |
41 | ## PERFORM LINT WITH FLAKE8
42 | - name: Lint with flake8
43 | run: |
44 | # stop the build if there are Python syntax errors or undefined names
45 | flake8 ./src --count --select=E9,F63,F7,F82 --show-source --statistics
46 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
47 | flake8 ./src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
48 |
49 | ## DEFINE UNIT TESTING JOB
50 | testing:
51 | ## DEFINE JOB NAME
52 | name: "TESTING"
53 |
54 | ## WAIT FOR PREVIOUS JOB
55 | needs: linting
56 |
57 | ## DEFINE WORKER
58 | runs-on: ubuntu-latest
59 | strategy:
60 | matrix:
61 | python-version: [3.8, 3.9]
62 |
63 | ## DEFINE STEPS
64 | steps:
65 | ## CLONE REPO CODE
66 | - uses: actions/checkout@v3
67 |
68 | ## SETUP PYTHON3
69 | - name: Set up Python ${{ matrix.python-version }}
70 | uses: actions/setup-python@v3
71 | with:
72 | python-version: ${{ matrix.python-version }}
73 |
74 | ## INSTALL DEPENDENCIES ON WORKER
75 | - name: Install Dependencies
76 | run: |
77 | python -m pip install --upgrade pip
78 | pip install pytest pytest-cov
79 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
80 |
81 | ## PERFORM UNIT TEST WITH PYTEST
82 | - name: Test with pytest
83 | run: |
84 | pytest -v --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html
85 |
86 | ## DEFINE CODE COVERAGE JOB
87 | code-cov:
88 | ## DEFINE NAME:
89 | name: "CODE COVERAGE"
90 |
91 | ## WAIT FOR PREVIOUS JOB
92 | needs: testing
93 |
94 | ## DEFINE WORKER
95 | runs-on: ubuntu-latest
96 |
97 | ## DEFINE ENVIRONMENT VARIABLES
98 | env:
99 | COVERAGE_SINGLE: 60 ## MINIMUM COVERAGE PERCENTAGE PER FILE
100 | COVERAGE_TOTAL: 60 ## MINIMUM COVERAGE PERCENTAGE TOTAL
101 |
102 | ## DEFINE STEPS
103 | steps:
104 | ## CLONE REPO CODE
105 | - uses: actions/checkout@v3
106 |
107 | ## SETUP PYTHON
108 | - name: Set up Python 3.9
109 | uses: actions/setup-python@v3
110 | with:
111 | python-version: 3.9
112 |
113 | ## INSTALL DEPENDENCIES
114 | - name: Install dependencies
115 | run: |
116 | python -m pip install --upgrade pip
117 | pip install flake8 pytest xmltodict
118 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
119 |
120 | ## PERFORM CODE COVERAGE TEST
121 | - name: "PyTest & CodeCov"
122 | id: pytest
123 | uses: programmingwithalex/pytester-cov@v1.2.4
124 | with:
125 | #pytest-root-dir: './src/'
126 | cov-omit-list: 'test/*, dev/*, docs/*, src/version.py'
127 | cov-threshold-single: ${{ env.COVERAGE_SINGLE }}
128 | cov-threshold-total: ${{ env.COVERAGE_TOTAL }}
129 |
130 | ## ADD RESULTS TO COMMIT
131 | #- name: Commit pytest coverage table
132 | # uses: peter-evans/commit-comment@v1
133 | # with:
134 | # body: ${{ steps.pytest.outputs.output-table }}
135 |
136 | ## SEND NOTIFICATION TO SLACK
137 | #- name: Slack Notification
138 | # uses: rtCamp/action-slack-notify@v2
139 | # env:
140 | # SLACK_TITLE: 'CodeCov Test Results'
141 | # SLACK_MESSAGE: ${{ steps.pytest.outputs.output-table }}
142 | # SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
143 |
144 | ## DEFINE SECURITY JOB
145 | security:
146 | ## DEFINE NAME
147 | name: "SECURITY"
148 |
149 | ## WAIT FOR PREVIOUS JOB
150 | needs: code-cov
151 |
152 | ## DEFINE WORKER
153 | runs-on: ubuntu-latest
154 |
155 | ## SKIP IF PR WAS CREATED BY DEPENDABOT (PERMISSIONS ISSUE)
156 | if: (github.actor != 'dependabot[bot]')
157 |
158 | ## DEFINE STEPS
159 | steps:
160 | ## CLONE REPO CODE
161 | - uses: actions/checkout@v3
162 |
163 | ## CACHE VULNERABILITY DATABASE
164 | - name: Cache multiple paths
165 | uses: actions/cache@v3
166 | with:
167 | path: |
168 | ${{ github.workspace }}/db
169 | key: ${{ runner.os }}-${{ hashFiles('requirements.txt') }}
170 |
171 | ## PERFORM SECURITY SCANS
172 | - name: Perform Scan
173 | uses: ShiftLeftSecurity/scan-action@master
174 | env:
175 | VDB_HOME: ${{ github.workspace }}/db
176 | WORKSPACE: ""
177 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
178 | with:
179 | output: reports
180 |
181 | ## UPLOAD RESULTS TO GITHUB SECURITY TAB
182 | - name: Upload SARIF file for GitHub Advanced Security Dashboard
183 | uses: github/codeql-action/upload-sarif@v2
184 | with:
185 | sarif_file: reports
186 | ## DEFINE NAME
187 | #name: "Build & Publish"
188 |
189 | ## WAIT FOR SAST JOBS
190 | #needs: [ "sast-bandit", "sast-semgrep" ]
191 |
192 | ## DEFINE WORKER
193 | #runs-on: ubuntu-latest
194 |
195 | ## DEFINE STEPS
196 | #steps:
197 | ## CLONE REPO CODE
198 | #- uses: actions/checkout@v3
199 |
200 | ## SETUP PYTHON3
201 | #- uses: actions/setup-python@v3
202 | # with:
203 | # python-version: 3.8
204 |
205 | ## CONFIGURE WORKER TO PUBLISH PACKAGE
206 | #- name: "Install Dependencies"
207 | # run: |
208 | # # Upgrade pip
209 | # python3 -m pip install --upgrade pip
210 | # Install Dependencies
211 | # python3 -m pip install --upgrade build xmltodict
212 |
213 | ## BUILD PACKAGE
214 | #- name: "Build Python Package"
215 | # run: |
216 | # # Build Python Package
217 | # python3 -m build
218 |
219 | ## PUBLISH PACKAGE TO TESTPYPI
220 | #- name: "Publish Package to Test PyPi"
221 | #if: github.repository == 'pkeech/stig_parser' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
222 | # uses: pypa/gh-action-pypi-publish@master
223 | # with:
224 | # password: ${{ secrets.TEST_PYPI_TOKEN }}
225 | # repository_url: https://test.pypi.org/legacy/
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## VSCODE
2 | .vscode/
3 |
4 | ## IGNORE STIGS USED FOR TESTING
5 | *.zip
6 |
7 | # Byte-compiled / optimized / DLL files
8 | __pycache__/
9 | *.py[cod]
10 | *$py.class
11 |
12 | # C extensions
13 | *.so
14 |
15 | # Distribution / packaging
16 | .Python
17 | build/
18 | develop-eggs/
19 | dist/
20 | downloads/
21 | eggs/
22 | .eggs/
23 | lib/
24 | lib64/
25 | parts/
26 | sdist/
27 | var/
28 | wheels/
29 | pip-wheel-metadata/
30 | share/python-wheels/
31 | *.egg-info/
32 | .installed.cfg
33 | *.egg
34 | MANIFEST
35 |
36 | # PyInstaller
37 | # Usually these files are written by a python script from a template
38 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
39 | *.manifest
40 | *.spec
41 |
42 | # Installer logs
43 | pip-log.txt
44 | pip-delete-this-directory.txt
45 |
46 | # Unit test / coverage reports
47 | htmlcov/
48 | .tox/
49 | .nox/
50 | .coverage
51 | .coverage.*
52 | .cache
53 | nosetests.xml
54 | coverage.xml
55 | *.cover
56 | *.py,cover
57 | .hypothesis/
58 | .pytest_cache/
59 |
60 | # Translations
61 | *.mo
62 | *.pot
63 |
64 | # Django stuff:
65 | *.log
66 | local_settings.py
67 | db.sqlite3
68 | db.sqlite3-journal
69 |
70 | # Flask stuff:
71 | instance/
72 | .webassets-cache
73 |
74 | # Scrapy stuff:
75 | .scrapy
76 |
77 | # Sphinx documentation
78 | docs/_build/
79 |
80 | # PyBuilder
81 | target/
82 |
83 | # Jupyter Notebook
84 | .ipynb_checkpoints
85 |
86 | # IPython
87 | profile_default/
88 | ipython_config.py
89 |
90 | # pyenv
91 | .python-version
92 |
93 | # pipenv
94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
97 | # install all needed dependencies.
98 | #Pipfile.lock
99 |
100 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
101 | __pypackages__/
102 |
103 | # Celery stuff
104 | celerybeat-schedule
105 | celerybeat.pid
106 |
107 | # SageMath parsed files
108 | *.sage.py
109 |
110 | # Environments
111 | .env
112 | .venv
113 | env/
114 | venv/
115 | ENV/
116 | env.bak/
117 | venv.bak/
118 |
119 | # Spyder project settings
120 | .spyderproject
121 | .spyproject
122 |
123 | # Rope project settings
124 | .ropeproject
125 |
126 | # mkdocs documentation
127 | /site
128 |
129 | # mypy
130 | .mypy_cache/
131 | .dmypy.json
132 | dmypy.json
133 |
134 | # Pyre type checker
135 | .pyre/
136 |
--------------------------------------------------------------------------------
/.semgrepignore:
--------------------------------------------------------------------------------
1 | ## SEMGREP IGNORE FILE
2 |
3 | ## IGNORE DIRECTORIES
4 | dev/
5 | docs/
6 | tests/
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [1.0.0] - 2020-09-24
8 |
9 | ### Added
10 |
11 | * Initial Release of STIG Parser
12 |
13 | ## [1.0.1] - 2020-12-31
14 |
15 | ### Added
16 |
17 | * Updated logic to handle parsing of new STIG schema. Resolved Issue [#3](https://github.com/pkeech/stig_parser/issues/3).
18 |
19 |
20 | ## [1.0.2] - 2021-06-14
21 |
22 | ### Added
23 |
24 | * Updated README
25 | * Additional fields to STIG JSON object;
26 | * Release Info
27 | * Source
28 | * Notice
29 | * Classification
30 | * CCI Number
31 | * STIG ID
32 | * Rule ID
33 |
34 | ## [1.1.0] - 2021-06-14
35 |
36 | ### Added
37 |
38 | * `extract_stig` function to extract the manual stig from a STIG ZIP.
39 | * `convert_stig` function to convert a STIG ZIP to a STIG JSON file.
40 | * `generate-ckl` function to generate a blank checklist object based upon the STIG passed to it.
41 | * `generate_ckl_file` function to generate a blank checklist based upon a checklist object passed to it.
42 | * Additional fields to STIG JSON object;
43 | * VulnID
44 | * RuleID
45 | * StigID
46 | * Severity
47 | * Cat
48 | * GroupTitle
49 | * RuleTitle
50 | * Description
51 | * FalsePositives
52 | * FalseNegatives
53 | * Documentable
54 | * Mitigations
55 | * SeverityOverrideGuidance
56 | * PotentialImpacts
57 | * ThirdPartyTools
58 | * MitigationControl
59 | * Responsibility
60 | * IAControls
61 | * CheckText
62 |
63 | ### Changed
64 |
65 | * Standardized Variable Names.
66 | * Standardized JSON output Variables.
67 |
68 | ## [1.1.1] - 2022-05-11
69 |
70 | ### Fixed
71 |
72 | * ([#37](https://github.com/pkeech/stig_parser/issues/37)) Resolved Issues Concerning STIG Rules with Multiple CCIs. Credit: @gregelin
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Peter Keech
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkeech/stig_parser/e8b5fd92e382148c4f806530f36878fa1723dc1e/MANIFEST.in
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Explore the docs »
10 |
11 |
12 | Report Bug
13 | ·
14 | Request Feature
15 |
16 |
17 |
18 |
19 | ![GitHub last commit][commit-shield]
20 | [![PyPi][pypi-shield]][pypi-url]
21 | [![GitHub Workflow Status][workflow-shield]][workflow-url]
22 | [![GitHub Open Issues][issues-shield]][issues-url]
23 | [![GitHub Open PRs][pr-shield]][pr-url]
24 | ![Python Versions][python-version-shield]
25 | [![GitHub License][license-shield]][license-url]
26 | [![LinkedIN Profile][linkedin-shield]][linkedin-url]
27 |
28 | > **NOTE**: As of version 1.1.0, the JSON output fields have been renamed and CamelCased. This was an effort to standardize the variables being used. When using Versions less than 1.1.0, please ensure you update your field names prior to updating.
29 |
30 | ### About
31 | A basic Python package to parse DISA STIGs (XCCDF) into a readable JSON format.
32 |
33 | ### Installation
34 | To install stig-parser, simple run the following command:
35 |
36 | `pip install stig-parser`
37 |
38 | ### Version Updates
39 | The table below briefly describes each update. For more information, view the releases page.
40 |
41 | | Version | Description |
42 | | :---: | --- |
43 | | 1.0.0 | Initial Creation of **stig-parser** |
44 | | 1.0.1 | Updated to handle change to STIG schema ([Issue #3](https://github.com/pkeech/stig_parser/issues/3)) |
45 | | 1.0.2 | Added Additional Fields to Output JSON. View Release Notes for Full Details ([Issue #9](https://github.com/pkeech/stig_parser/issues/9))|
46 | | 1.1.0 | Added Additional Fields to Output JSON, Included BETA release of CKL creation and added the ability to parse a STIG directly from the ZIP file. View Release Notes for Full Details |
47 | | 1.1.1 | Resolved Issues Concerning STIG Rules with Multiple CCIs. Credit: @gregelin |
48 |
49 | ### Documentation
50 | Documentation hasn't been created at this time. For the current development documentation, please visit the [repository](https://github.com/pkeech/stig_parser).
51 |
52 | ### Testing
53 | This project leverages GitHub Actions for its CI/CD workflow. During a Push to any branch, with the exception of `Master` and `Dev`, the workflow will perform Unit Testing and Linting.
54 |
55 | For manual testing, run the following commands;
56 |
57 | ``` bash
58 | ## START PYTHON DEV CONTAINER
59 | docker run -it --rm -v $(PWD):/stig-parser python /bin/bash
60 |
61 | ## INSTALL DEPENDENCIES
62 | pip install pytest pytest-cov xmltodict
63 |
64 | ## CHANGE WORKING DIRECTORY
65 | cd stig-parser
66 |
67 | ## RUN PYTEST
68 | pytest -v
69 |
70 | ## RUN PYTEST COVERAGE
71 | pytest --cov src
72 | ```
73 |
74 | ### Usage
75 | This module contains the following functions;
76 |
77 | | Function | Description | Parameters |
78 | | --- | --- | --- |
79 | | `convert_stig(STIG_FILE)` | This function will extract the STIG from a ZIP archive, and parse the results into a JSON object | `STIG_FILE` == Path to STIG ZIP File |
80 | | `convert_xccdf(STIG_XML)` | This function will parse a raw bytes of a STIG file (XML) and return a JSON object| `STIG_XML` == Bytes object of STIG xccdf.xml File |
81 | | `generate_stig_json(STIG_JSON, EXPORT_PATH)` | This function will write the STIG JSON object to a File | `STIG_JSON` == JSON Object of STIG, `EXPORT_PATH` == Path to create JSON File |
82 | | `generate_ckl(STIGFILE, CHECKLIST_INFO)` | This function will generate an XML Object of a CKL based upon a passed STIG | `STIG_FILE` == Path to STIG ZIP File , `CHECKLIST_INFO` == JSON Object of additional information needed (see below) |
83 | | `generate_ckl_file(CKL, EXPORT_PATH)` | This function will write the CKL XML Object to a File | `CKL` == XML Object of CKL , `EXPORT_PATH` == Path to create CKL File |
84 |
85 | When creating a Checklist (CKL), additional information is required. This information is added to the CKL but is required to be defined prior to creation. For an example of usage, please see the examples below.
86 |
87 | ``` json
88 | {
89 | "ROLE": "None",
90 | "ASSET_TYPE": "Computing",
91 | "HOST_NAME": "Test_Host",
92 | "HOST_IP": "1.2.3.4",
93 | "HOST_MAC": "",
94 | "HOST_FQDN": "test.hostname.dev",
95 | "TARGET_COMMENT": "",
96 | "TECH_AREA": "",
97 | "TARGET_KEY": "3425",
98 | "WEB_OR_DATABASE": "false",
99 | "WEB_DB_SITE": "",
100 | "WEB_DB_INSTANCE": ""
101 | }
102 | ```
103 |
104 | ### Examples
105 | This module has several use cases that will either generate a JSON object of a STIG file, or an XML object of a CKL file.
106 |
107 | #### STIGs
108 | To convert a STIG file to a JSON object, you can utilize the following example.
109 |
110 | ``` python
111 | ## LOAD PYTHON MODULE
112 | from stig_parser import convert_stig
113 |
114 | ## PARSE STIG ZIP FILE
115 | ## ASSUMES ZIP FILE IS IN CURRENT WORKING DIRECTORY
116 | json_results = convert_stig('./U_Docker_Enterprise_2-x_Linux-UNIX_V1R1_STIG.zip')
117 | ```
118 |
119 | Additionally, this example demonstrates how to generate the STIG JSON object from an **xccdf** file.
120 |
121 | ``` python
122 | ## LOAD PYTHON MODULE
123 | from stig_parser import convert_xccdf
124 |
125 | ## LOAD XML FILE (OPTIONAL)
126 | import os
127 |
128 | with open("U_Docker_Enterprise_2-x_Linux-UNIX_STIG_V1R1_Manual-xccdf.xml", "r") as fh:
129 | raw_file = fh.read()
130 |
131 | ## PARSE XCCDF(XML) to JSON
132 | json_results = convert_xccdf(raw_file)
133 | ```
134 |
135 | #### Checklists (CKL)
136 | To generate a CKL from a given STIG, you can utilize the following example;
137 |
138 | ``` python
139 | ## LOAD PYTHON MODULE
140 | from stig_parser import generate_ckl, generate_ckl_file
141 |
142 | ## DEFINE STIG FILE LOCATION
143 | ## ASSUMES ZIP FILE IS IN CURRENT WORKING DIRECTORY
144 | STIG = './U_Docker_Enterprise_2-x_Linux-UNIX_V1R1_STIG.zip'
145 |
146 | ## DEFINE EXPORT LOCATION
147 | EXPORT = './ myCKL.ckl'
148 |
149 | ## DEFINE ADDITIONAL CHECKLIST INFORMATION
150 | CHECKLIST_INFO ={
151 | "ROLE": "None",
152 | "ASSET_TYPE": "Computing",
153 | "HOST_NAME": "Test_Host",
154 | "HOST_IP": "1.2.3.4",
155 | "HOST_MAC": "",
156 | "HOST_FQDN": "test.hostname.dev",
157 | "TARGET_COMMENT": "",
158 | "TECH_AREA": "",
159 | "TARGET_KEY": "3425",
160 | "WEB_OR_DATABASE": "false",
161 | "WEB_DB_SITE": "",
162 | "WEB_DB_INSTANCE": ""
163 | }
164 |
165 |
166 | ## GENERATE CKL XML OBJECT
167 | RAW_CKL = generate_ckl(STIG, CHECKLIST_INFO)
168 |
169 | ## SAVE CHECKLIST TO FILE
170 | generate_ckl_file(RAW_CKL, EXPORT)
171 | ```
172 |
173 |
174 | ### Output
175 | Outlined below is the expected JSON output:
176 |
177 | ``` json
178 | {
179 | "Title": "xxxxxxx",
180 | "Description": "xxxxxxx",
181 | "Version": "x",
182 | "Release": "x ",
183 | "BenchmarkDate": "xxxxxxx",
184 | "ReleaseInfo": "xxxxxxx",
185 | "Source": "xxxxxxx",
186 | "Notice": "xxxxxxx",
187 | "Rules": [
188 | {
189 | "VulnID": "xxxxxxx",
190 | "RuleID": "xxxxxxx",
191 | "StigID": "xxxxxxx",
192 | "Severity": "high | medium | low",
193 | "Cat": "CAT I | CAT II | CAT III",
194 | "Classification": "",
195 | "GroupTitle": "xxxxxxx",
196 | "RuleTitle": "xxxxxxx",
197 | "Description": "xxxxxxx",
198 | "VulnDiscussion": "xxxxxxx",
199 | "FalsePositives": "xxxxxxx",
200 | "FalseNegatives": "xxxxxxx",
201 | "Documentable": "xxxxxxx",
202 | "Mitigations": "xxxxxxx",
203 | "SeverityOverrideGuidance": "xxxxxxx",
204 | "PotentialImpacts": "xxxxxxx",
205 | "ThirdPartyTools": "xxxxxxx",
206 | "MitigationControl": "xxxxxxx",
207 | "Responsibility": "xxxxxxx",
208 | "IAControls": "xxxxxxx",
209 | "CheckText": "xxxxxxx",
210 | "FixText": "xxxxxxx",
211 | "CCI": "xxxxxxx"
212 | }
213 | ]
214 | }
215 | ```
216 |
217 |
218 | ### Dependencies
219 | The following packages are required for this package:
220 |
221 | | Package Name | Reason |
222 | | :---: | --- |
223 | | xmltodict | This converts the raw XML file to a python dictionary for ease of processing |
224 |
225 | ### Comments, Concerns and Gripes
226 | If you have any comments, concerns and/or gripes, please feel free to submit an issue on the [repository](https://github.com/pkeech/stig_parser).
227 |
228 |
229 | [commit-shield]: https://img.shields.io/github/last-commit/pkeech/stig_parser?style=for-the-badge
230 | [pypi-shield]: https://img.shields.io/pypi/v/stig-parser?style=for-the-badge
231 | [pypi-url]: https://pypi.org/project/stig-parser/
232 | [workflow-shield]: https://img.shields.io/github/workflow/status/pkeech/stig_parser/Build%20&%20Deploy%20PyPi%20Package?style=for-the-badge
233 | [workflow-url]: https://github.com/pkeech/stig_parser/actions
234 |
235 |
236 | [issues-shield]: https://img.shields.io/github/issues/pkeech/stig_parser?style=for-the-badge
237 | [issues-url]: https://github.com/pkeech/stig_parser/issues
238 | [pr-shield]: https://img.shields.io/github/issues-pr/pkeech/stig_parser?style=for-the-badge
239 | [pr-url]: https://github.com/pkeech/stig_parser/pulls
240 | [python-version-shield]: https://img.shields.io/pypi/pyversions/stig-parser?style=for-the-badge
241 | [license-shield]: https://img.shields.io/github/license/pkeech/stig_parser?style=for-the-badge
242 | [license-url]: https://github.com/pkeech/stig_parser/blob/master/LICENSE
243 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
244 | [linkedin-url]: https://www.linkedin.com/in/peter-keech-b88183a2/
245 |
--------------------------------------------------------------------------------
/docs/examples/U_Docker_Enterprise_2-x_Linux-UNIX_V1R1_STIG.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkeech/stig_parser/e8b5fd92e382148c4f806530f36878fa1723dc1e/docs/examples/U_Docker_Enterprise_2-x_Linux-UNIX_V1R1_STIG.zip
--------------------------------------------------------------------------------
/docs/images/STIG_Parser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkeech/stig_parser/e8b5fd92e382148c4f806530f36878fa1723dc1e/docs/images/STIG_Parser.png
--------------------------------------------------------------------------------
/docs/mapping.md:
--------------------------------------------------------------------------------
1 | # CKL to STIG Mappings
2 |
3 | ### CKL Layout
4 |
5 | ``` xml
6 |
7 |
8 |
9 |
10 | ...
11 |
12 |
13 |
14 |
15 | ...
16 |
17 |
18 | ...
19 |
20 |
21 |
22 |
23 | ```
24 |
25 |
26 |
27 | ### Asset Breakdown
28 |
29 | **Layout**
30 | ``` xml
31 |
32 | None
33 | Computing
34 | Test Hostname
35 | 1.1.1.1
36 | 0A:0A:0A:0A:0A
37 | test.hostname.dev
38 |
39 |
40 | 3425
41 | false
42 |
43 |
44 |
45 | ```
46 |
47 | **Fields**
48 |
49 | | Field | CKL FILE | STIG FILE | VALUES |
50 | | :---: | --- | --- | :---: |
51 | | Role | `````` | - | `None`, `Workstation`, `Member Server`, `Domain Controller` |
52 | | Asset Type | `````` | - | `Computing`, `Non-Computing` |
53 | | Host Name | `````` | - | Free Text |
54 | | Host IP | `````` | - | Free Text |
55 | | Host MAC | `````` | - | Free Text |
56 | | Host FQDN | `````` | - | Free Text |
57 | | Target Comment | `````` | - | Free Text |
58 | | Tech Area | `````` | - | `Application Review`, `Boundary Security`, `CDS Admin Review`, `CDS Technical Review`, `Database Review`, `Domain Name System (DNS)`, `Exchange Server`, `Host Based System Security (HBSS)`, `Internal Network`, `Mobility`, `Releasable Networks (REL)`, `Traditional Security`, `UNIX OS`, `VVOIP Review`, `Web Review`, `Windows OS`, `Other Review` |
59 | | Target Key | `````` | - | Free Text |
60 | | Web or DB | `````` | - | `true`, `false` |
61 | | Web/DB Site | `````` | - | Free Text |
62 | | Web/DB Instance | `````` | - | Free Text |
63 |
64 | ### STIG Breakdown
65 |
66 | **Layout**
67 | ``` xml
68 |
69 |
70 |
71 |
72 | version
73 | 1
74 |
75 |
76 | classification
77 | UNCLASSIFIED
78 |
79 |
80 | customname
81 |
82 |
83 | stigid
84 | Docker_Enterprise_2-x_Linux-UNIX_STIG
85 |
86 |
87 | description
88 | This Security Technical Implementation Guide is published as a tool to improve the security of Department of Defense (DoD) information systems. The requirements are derived from the National Institute of Standards and Technology (NIST) 800-53 and related documents. Comments or proposed revisions to this document should be sent via email to the following address: disa.stig_spt@mail.mil.
89 |
90 |
91 | filename
92 | U_Docker_Enterprise_2-x_Linux-UNIX_STIG_V1R1_Manual-xccdf.xml
93 |
94 |
95 | releaseinfo
96 | Release: 1 Benchmark Date: 19 Jul 2019
97 |
98 |
99 | title
100 | Docker Enterprise 2.x Linux/UNIX Security Technical Implementation Guide
101 |
102 |
103 | uuid
104 | eaab6cca-d77d-4787-868b-766afc44845d
105 |
106 |
107 | notice
108 | terms-of-use
109 |
110 |
111 | source
112 |
113 |
114 |
115 |
116 | Vuln_Num
117 | V-94863
118 |
119 |
120 | Severity
121 | low
122 |
123 |
124 | Group_Title
125 | SRG-APP-000001
126 |
127 |
128 | Rule_ID
129 | SV-104693r1_rule
130 |
131 |
132 | Rule_Ver
133 | DKER-EE-001000
134 |
135 |
136 | Rule_Title
137 | The Docker Enterprise Per User Limit Login Session Control in the Universal Control Plane (UCP) Admin Settings must be set to an organization-defined value for all accounts and/or account types.
138 |
139 |
140 | Vuln_Discuss
141 | The UCP component of Docker Enterprise includes a built-in access authorization mechanism called eNZi which can be integrated with an LDAP server and subsequently configured to limit the number of concurrent sessions to an organization-defined number for all accounts and/or account types. Per-user session control limits are configured with a default of 10. For reference, the per user limit in UCP specifies the maximum number of sessions that any user can have active at any given time. If creating a new session would put a user over this limit then the least recently used session will be deleted. A value of zero disables limiting the number of sessions that users may have. This configuration applies to both the UCP and DTR management consoles.
142 |
143 |
144 | IA_Controls
145 |
146 |
147 |
148 | Check_Content
149 | Check that the "Per User Limit" Login Session Control in the UCP Admin Settings is set according to the values defined in the System Security Plan.
150 |
151 | via UI:
152 |
153 | In the UCP web console, navigate to "Admin Settings" | "Authentication & Authorization" and verify the "Per User Limit" field is set according to the number specified in the System Security Plan.
154 |
155 | via CLI:
156 |
157 | Linux (requires curl and jq): As a Docker EE Admin, execute the following commands from a machine with connectivity to the UCP management console. Replace [ucp_url] with the UCP URL, [ucp_username] with the username of a UCP administrator and [ucp_password] with the password of a UCP administrator.
158 |
159 | AUTHTOKEN=$(curl -sk -d '{"username":"[ucp_username]","password":"[ucp_password]"}' https://[ucp_url]/auth/login | jq -r .auth_token)
160 | curl -sk -H "Authorization: Bearer $AUTHTOKEN" https://[ucp_url]/api/ucp/config-toml|grep per_user_limit
161 |
162 | If the "per_user_limit" entry under the "[auth.sessions]" section in the output is not set according to the value defined in the SSP, this is a finding.
163 |
164 |
165 | Fix_Text
166 | Set the "Per User Limit" Login Session Control in the UCP Admin Settings per the requirements set forth by the System Security Plan (SSP).
167 |
168 | via UI:
169 |
170 | In the UCP web console, navigate to "Admin Settings" | "Authentication & Authorization" and set the "Per User Limit" field according to the requirements of this control.
171 |
172 | via CLI:
173 |
174 | Linux (requires curl and jq): As a Docker EE Admin, execute the following commands on either a UCP Manager node or using a UCP client bundle. Replace [ucp_url] with the UCP URL, [ucp_username] with the username of a UCP administrator and [ucp_password] with the password of a UCP administrator.
175 |
176 | AUTHTOKEN=$(curl -sk -d '{"username":"[ucp_username]","password":"[ucp_password]"}' https://[ucp_url]/auth/login | jq -r .auth_token)
177 | curl -sk -H "Authorization: Bearer $AUTHTOKEN" https://[ucp_url]/api/ucp/config-toml > ucp-config.toml
178 |
179 | Open the "ucp-config.toml" file, set the "per_user_limit" entry under the "[auth.sessions]" section according to the requirements of this control. Save the file.
180 |
181 | Execute the following commands to update UCP with the new configuration:
182 |
183 | curl -sk -H "Authorization: Bearer $AUTHTOKEN" --upload-file ucp-config.toml https://[ucp_url]/api/ucp/config-toml
184 |
185 |
186 | False_Positives
187 |
188 |
189 |
190 | False_Negatives
191 |
192 |
193 |
194 | Documentable
195 | false
196 |
197 |
198 | Mitigations
199 |
200 |
201 |
202 | Potential_Impact
203 |
204 |
205 |
206 | Third_Party_Tools
207 |
208 |
209 |
210 | Mitigation_Control
211 |
212 |
213 |
214 | Responsibility
215 |
216 |
217 |
218 | Security_Override_Guidance
219 |
220 |
221 |
222 | Check_Content_Ref
223 | M
224 |
225 |
226 | Weight
227 | 10.0
228 |
229 |
230 | Class
231 | Unclass
232 |
233 |
234 | STIGRef
235 | Docker Enterprise 2.x Linux/UNIX Security Technical Implementation Guide :: Version 1, Release: 1 Benchmark Date: 19 Jul 2019
236 |
237 |
238 | TargetKey
239 | 3425
240 |
241 |
242 | STIG_UUID
243 | 9dc3babd-6054-4e33-a4e8-a939ec0b2fc8
244 |
245 |
246 | LEGACY_ID
247 |
248 |
249 |
250 | LEGACY_ID
251 |
252 |
253 |
254 | CCI_REF
255 | CCI-000054
256 |
257 | NotAFinding
258 | Test Finding Details.
259 |
260 | This has been set to "Not a Finding"
261 | This are my test comments
262 |
263 |
264 |
265 | ```
266 |
267 | **Fields (STIG Data)**
268 |
269 | | Field | CKL FILE | STIG FILE |
270 | | :---: | --- | --- |
271 | | Version | ```version1``` | ```1```|
272 | | Classification | ```classificationUNCLASSIFIED```| ??? |
273 | | Custom Name | ```customname``` | ??? |
274 | | STIG ID | ```stigidDocker_Enterprise_2-x_Linux-UNIX_STIG``` | `````` |
275 | | Description | ```descriptionThis Security Technical ...``` | ```This Security Technical Implementation ...>``` |
276 | | Filename | ```filenameU_Docker_Enterprise_2-x_Linux-UNIX_STIG_V1R1_Manual-xccdf.xml``` | From Passed STIG File |
277 | | Release Info | ```releaseinfoRelease: 1 Benchmark Date: 19 Jul 2019``` | ```Release: 1 Benchmark Date: 19 Jul 2019``` |
278 | | Title | ```titleDocker Enterprise 2.x Linux/UNIX Security Technical Implementation Guide``` | ```Docker Enterprise 2.x Linux/UNIX Security Technical Implementation Guide``` |
279 | | UUID | ```uuideaab6cca-d77d-4787-868b-766afc44845d``` | ??? |
280 | | Notice | ```noticeterms-of-use``` | `````` |
281 |
282 |
283 |
284 | **Fields (Vulnerabilities)**
285 |
286 | | Field | CKL FILE | STIG FILE |
287 | | :---: | --- | --- |
288 | | Vulnerability Number | ```Vuln_NumV-94863``` | `````` |
289 | | Severity | ```Severitylow``` | `````` |
290 | | Group Title | ```Group_TitleSRG-APP-000001``` | ```SRG-APP-000001``` |
291 | | Rule ID | ```Rule_IDSV-104693r1_rule``` | `````` |
292 | | Rule Version | ```Rule_VerDKER-EE-001000``` | ```DKER-EE-001000``` |
293 | | Rule Title | ```Rule_TitleThe Docker Enterprise ... ``` | ```The Docker Enterprise ... ``` |
294 | | Vulnerability Discussion | ```Vuln_DiscussThe UCP ...``` | ```<VulnDiscussion>The UCP ...</VulnDiscussion>...``` |
295 | | IA Controls | ```IA_Controls``` | ```...<IAControls></IAControls>``` |
296 | | Check Content | ```Check_ContentCheck that the ...>``` | ```Check that the ...>``` |
297 | | Fix Text | ```Fix_TextSet the "Per ...``` | ```Set the "Per ...>``` |
298 | | False Positives | ```False_Positives``` | ``` ``` |
299 | | False Negatives | ``` ``` | ```...<FalseNegatives></FalseNegatives><...>``` |
300 | | Documentable | ``` ``` | ```...<Documentable></Documentable><...>``` |
301 | | Mitigations | ``` ``` | ``` ``` |
302 | | Potential Impact | ``` ``` | ``` ``` |
303 | | Third Party Tools | ``` ``` | ``` ``` |
304 | | Mitigation Control | ``` ``` | ``` ``` |
305 | | Responsibility | ``` ``` | ``` ``` |
306 | | Security Override Guidance | ``` ``` | ``` ``` |
307 | | Check Content Reference | ``` ``` | ``` ``` |
308 | | Weight | ``` ``` | ``` ``` |
309 | | Classification | ``` ``` | ``` ``` |
310 | | STIG Reference | ``` ``` | ``` ``` |
311 | | Target Key | ``` ``` | ``` ``` |
312 | | STIG UUID | ``` ``` | ``` ``` |
313 | | Legacy ID | ``` ``` | ``` ``` |
314 | | Legacy ID | ``` ``` | ``` ``` |
315 | | CCI Reference | ``` ``` | ``` ``` |
316 | | Status | ``` ``` | ``` ``` |
317 | | Finding Details | ``` ``` | ``` ``` |
318 | | Comments | ``` ``` | ``` ``` |
319 | | Severity Override | ``` ``` | ``` ``` |
320 | | Severity Justification | ``` ``` | ``` ``` |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | attrs==21.2.0
2 | coverage==5.5
3 | iniconfig==1.1.1
4 | packaging==21.0
5 | pluggy==0.13.1
6 | py==1.10.0
7 | pyparsing==2.4.7
8 | pytest==6.2.4
9 | pytest-cov==2.12.1
10 | toml==0.10.2
11 | xmltodict==0.12.0
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | ## IMPORT REQUIRED DOCUMENTS
2 | import setuptools, subprocess, os
3 |
4 | ## PARSE README TO LONG DESCRIPTION
5 | with open("README.md", "r") as fh:
6 | long_description = fh.read()
7 |
8 | ## QUERY GITHUB FOR TAG TO GENERATE VERSION
9 | new_version = (
10 | subprocess.run(["git", "describe", "--tags"], cwd="/home/runner/work/stig_parser/stig_parser", stdout=subprocess.PIPE)
11 | .stdout.decode("utf-8")
12 | .strip()
13 | )
14 |
15 | ## DEFINE PACKAGE VERSION
16 | assert "." in new_version
17 | assert os.path.isfile("src/stig_parser/version.py")
18 | with open("src/stig_parser/VERSION", "w", encoding="utf-8") as fh:
19 | fh.write(f"{new_version}\n")
20 |
21 | ## DEFINE PACKAGE
22 | setuptools.setup(
23 | name='stig_parser',
24 | version=new_version,
25 | license='MIT',
26 | author="Peter Keech",
27 | author_email="peter.a.keech@gmail.com",
28 | description="A Python module to parse DISA STIG (XCCDF) Files",
29 | long_description=long_description,
30 | long_description_content_type="text/markdown",
31 | url="https://github.com/pkeech/stig_parser",
32 | packages=setuptools.find_packages('src'),
33 | package_dir={'': 'src'},
34 | install_requires=[
35 | 'xmltodict'
36 | ],
37 | classifiers=[
38 | "Programming Language :: Python :: 3.8",
39 | "Programming Language :: Python :: 3.9",
40 | "License :: OSI Approved :: MIT License",
41 | "Operating System :: OS Independent",
42 | ],
43 | python_requires='>=3.8'
44 | )
45 |
--------------------------------------------------------------------------------
/src/stig_parser/__init__.py:
--------------------------------------------------------------------------------
1 | ## EXPOSE FUNCTIONS
2 | from .stig_parser import convert_stig, convert_xccdf, generate_stig_json, generate_ckl, generate_ckl_file
--------------------------------------------------------------------------------
/src/stig_parser/stig_parser.py:
--------------------------------------------------------------------------------
1 | ## ==============================
2 | ## ===== STIG PARSER MODULE =====
3 | ## ==============================
4 |
5 | ## Created By : Peter Keech
6 | ## Email : peter.a.keech@gmail.com
7 | ## Version : 1.1.0
8 | ## Description : STIG-Parser 1.1.0 Module
9 | ## Requirements: xmltodict
10 |
11 | ## IMPORT REQUIRED EXTERNAL MODULES
12 | import os, xmltodict, zipfile, json, uuid
13 | import xml.etree.cElementTree as ET
14 |
15 | ## -----------------------------
16 | ## ----- PRIVATE FUNCTIONS -----
17 | ## -----------------------------
18 |
19 | ## FUNCTION: GENERATE STIG INFO ELEMENTS
20 | def generate_stig_info(PARENT, KEY, VALUE):
21 | SI_DATA = ET.SubElement(PARENT, 'SI_DATA')
22 | SID_NAME = ET.SubElement(SI_DATA, 'SID_NAME').text = KEY
23 |
24 | if VALUE != None:
25 | SID_DATA = ET.SubElement(SI_DATA, 'SID_DATA').text = VALUE
26 |
27 | ## FUNCTION: GENERATE VULN ELEMENTS
28 | def generate_vuln(PARENT, KEY, VALUE):
29 | STIG_DATA = ET.SubElement(PARENT, 'STIG_DATA')
30 | VULN_ATTRIBUTE = ET.SubElement(STIG_DATA, 'VULN_ATTRIBUTE').text = KEY
31 | ATTRIBUTE_DATA = ET.SubElement(STIG_DATA, 'ATTRIBUTE_DATA').text = VALUE
32 |
33 | ## FUNCTION: FIND BETWEEN TWO POINTS IN A STRING
34 | ## NEEDED DUE TO XML TAGS BEING CONTAINED WITHIN DESCRIPTION TEXT
35 | def find_between( s, first, last ):
36 | try:
37 | start = s.index( first ) + len( first )
38 | end = s.index( last, start )
39 | return s[start:end]
40 | except ValueError:
41 | return ""
42 |
43 | ## FUNCTION: PRETTY PRINT ELEMENTTREE OUTPUT
44 | ## THIS IS NOT NEEDED AS OF NOW
45 | ## TO USE, CALL FUNCTION
46 | ## PrettyPrint(ElementTree_Object)
47 | def PrettyPrint(elem, level=0):
48 | i = "\n" + level*" "
49 | j = "\n" + (level-1)*" "
50 | if len(elem):
51 | if not elem.text or not elem.text.strip():
52 | elem.text = i + " "
53 | if not elem.tail or not elem.tail.strip():
54 | elem.tail = i
55 | for subelem in elem:
56 | PrettyPrint(subelem, level+1)
57 | if not elem.tail or not elem.tail.strip():
58 | elem.tail = j
59 | else:
60 | if level and (not elem.tail or not elem.tail.strip()):
61 | elem.tail = j
62 | return elem
63 |
64 | ## FUNCTION: HANDLE STIG RULES WITH MULTIPLE CCI ENTRIES
65 | ## PROVIDED BY: gregelin
66 |
67 | def get_cci_list(IDENT):
68 | ## HANDLE MULTIPLE IDENT ENTRIES (CCI)
69 | if len(IDENT) == 2:
70 | IDENT = IDENT['#text']
71 | else:
72 | ## DEFINE EMPTY RESULTS
73 | RESULTS = ""
74 |
75 | ## LOOP THROUGH ALL CCI NUMBERS
76 | for RESULT in IDENT:
77 | RESULTS += RESULT['#text'] + ","
78 |
79 | ## REMOVE LAST ','
80 | IDENT = RESULTS.rstrip(RESULTS[-1])
81 |
82 | ## RETURN RESULTS
83 | return IDENT
84 |
85 | ## ----------------------------
86 | ## ----- PUBLIC FUNCTIONS -----
87 | ## ----------------------------
88 |
89 | ## FUNCTION: CONVERT RAW XCCDF (XML) TO JSON
90 | def convert_xccdf(RAW):
91 |
92 | ## CONVERT XML TO PYTHON DICTIONARY
93 | CONTENT_DICT = xmltodict.parse(RAW, dict_constructor=dict)
94 |
95 | ## HANDLE NEW VS. OLD VERSION OF STIG SCHEMA (ISSUE #3)
96 | if isinstance(CONTENT_DICT['Benchmark']['plain-text'], list):
97 | ## NEW VERSION
98 | RAW_VERSION = CONTENT_DICT['Benchmark']['plain-text'][0]['#text']
99 | else:
100 | ## OLD VERSION
101 | RAW_VERSION = CONTENT_DICT['Benchmark']['plain-text']['#text']
102 |
103 | ## SAVE RELEASE INFO
104 | RELEASE_INFO = RAW_VERSION
105 |
106 | ## FORMAT RELEASE INFO FOR SPECIFIC DATA FIELDS
107 | RAW_VERSION = RAW_VERSION.split('Benchmark Date: ')
108 | BENCH_DATE = RAW_VERSION[1]
109 | REL = RAW_VERSION[0].replace('Release: ','')
110 |
111 | ## CREATE RETURNED STIG JSON
112 | ## TODO: ADD STIG FILENAME
113 | JSON_RESULTS = {
114 | "Title": CONTENT_DICT['Benchmark']['title'],
115 | "Description": CONTENT_DICT['Benchmark']['description'],
116 | "Version": CONTENT_DICT['Benchmark']['version'],
117 | "Release": REL,
118 | "BenchmarkDate": BENCH_DATE,
119 | "ReleaseInfo": RELEASE_INFO,
120 | "Source": CONTENT_DICT['Benchmark']['reference']['dc:source'],
121 | "Notice": CONTENT_DICT['Benchmark']['notice']['@id']
122 | }
123 |
124 | ## GENERATE EMPTY ARRAY
125 | STIGS = []
126 |
127 | ## LOOP THROUGH STIGS
128 | for STIG in CONTENT_DICT['Benchmark']['Group']:
129 |
130 | ## PARSE IDENT
131 | IDENT = STIG['Rule']['ident']
132 |
133 | ## HANDLE ARRAY CASE
134 | IDENT_LIST = ""
135 | if type(IDENT) == list:
136 | for IDENT_ITEM in IDENT:
137 | IDENT_LIST += get_cci_list(IDENT_ITEM) + ","
138 | else:
139 | IDENT_LIST += get_cci_list(IDENT) + ","
140 | ## REMOVE LAST ','
141 | IDENT = IDENT_LIST.rstrip(IDENT_LIST[-1])
142 |
143 | ## FORMAT SEVERITY
144 | ## TODO: DOCUMENT WHY THIS IS HAPPENING. STIGVIEWER CONVERTS LOW/MED/HIGH TO CAT III/CAT II/CAT I
145 | if STIG['Rule']['@severity'] == "high":
146 | CAT = "CAT I"
147 | elif STIG['Rule']['@severity'] == "medium":
148 | CAT = "CAT II"
149 | elif STIG['Rule']['@severity'] == "low":
150 | CAT = "CAT III"
151 | else:
152 | CAT = ""
153 |
154 | ## DEFINE RULE STRUCTURE
155 | oSTIG = {
156 | 'VulnID': STIG['@id'],
157 | 'RuleID': STIG['Rule']['@id'],
158 | 'StigID': STIG['Rule']['version'],
159 | 'Severity': STIG['Rule']['@severity'],
160 | 'Cat': CAT,
161 | ## TODO: DETERMINE STIG RULE CLASSIFICATION
162 | 'Classification': "",
163 | 'GroupTitle': STIG['title'],
164 | 'RuleTitle': STIG['Rule']['title'],
165 | 'Description': STIG['Rule']['description'],
166 | 'VulnDiscussion': find_between(STIG['Rule']['description'], "", ""),
167 | 'FalsePositives': find_between(STIG['Rule']['description'], "", ""),
168 | 'FalseNegatives': find_between(STIG['Rule']['description'], "", ""),
169 | 'Documentable': find_between(STIG['Rule']['description'], "", ""),
170 | 'Mitigations': find_between(STIG['Rule']['description'], "", ""),
171 | 'SeverityOverrideGuidance': find_between(STIG['Rule']['description'], "", ""),
172 | 'PotentialImpacts': find_between(STIG['Rule']['description'], "", ""),
173 | 'ThirdPartyTools': find_between(STIG['Rule']['description'], "", ""),
174 | 'MitigationControl': find_between(STIG['Rule']['description'], "", ""),
175 | 'Responsibility': find_between(STIG['Rule']['description'], "", ""),
176 | 'IAControls': find_between(STIG['Rule']['description'], "", ""),
177 | 'CheckText': STIG['Rule']['check']['check-content'],
178 | 'FixText': STIG['Rule']['fixtext']['#text'],
179 | 'CCI': IDENT,
180 | }
181 |
182 | ## ADD TO ARRAY
183 | STIGS.append(oSTIG)
184 |
185 | ## ADD STIGS TO JSON OBJECT
186 | JSON_RESULTS['Rules'] = STIGS
187 |
188 | ## RETURN RESULTS
189 | return JSON_RESULTS
190 |
191 | ## FUNCTION: EXTRACT STIG FROM ZIP FILE
192 | def extract_stig(FILENAME):
193 | ## OPEN XML FILE FROM ZIP FILE AND OBTAIN LIST OF FILES
194 | ZIP = zipfile.ZipFile(FILENAME)
195 | FILES = ZIP.namelist()
196 |
197 | ## FIND MANUAL STIG FILE
198 | for FILE in FILES:
199 | if FILE.endswith('_Manual-xccdf.xml'):
200 | ## HANDLE MACOS
201 | if not FILE.startswith('__MACOS'):
202 | ## DETERMINE FILE NAME
203 | STIG_FILENAME = FILE
204 | break
205 |
206 | ## ENSURE FILE IS FOUND
207 | assert STIG_FILENAME is not None, 'Manual STIG File was NOT FOUND'
208 |
209 | ## READ STIG FILE
210 | RAW_FILE = ZIP.read(STIG_FILENAME)
211 |
212 | ## RETURN RAW STIG
213 | return RAW_FILE
214 |
215 | ## FUNCTION: CONVERT STIG (ZIP) TO JSON FILE
216 | def convert_stig(FILENAME):
217 | ## EXTRACT STIG FROM ZIP FILE
218 | RAW_STIG = extract_stig(FILENAME)
219 |
220 | ## CONVERT TO JSON
221 | RESULTS = convert_xccdf(RAW_STIG)
222 |
223 | ## RETURN JSON STIG
224 | return RESULTS
225 |
226 | ## FUNCTION: CREATE STIG JSON FILE
227 | def generate_stig_json(JSON_STIG, EXPORT_FILE):
228 | ## CREATE JSON FILE AND SAVE
229 | with open(EXPORT_FILE, 'w') as FILE:
230 | json.dump(JSON_STIG, FILE, indent=4)
231 |
232 | ## FUNCTION: GENERATE BLANK CHECKLIST
233 | def generate_ckl(FILENAME, CHECKLIST_INFO):
234 | ## EXTRACT STIG FROM ZIP FILE
235 | RAW_STIG = extract_stig(FILENAME)
236 |
237 | ## CONVERT TO JSON
238 | JSON_STIG = convert_xccdf(RAW_STIG)
239 |
240 | ## GENERATE STIG_UUID
241 | STIG_UUID = uuid.uuid4()
242 |
243 | ## GENERATE XML STRUCTURE
244 | ROOT = ET.Element('CHECKLIST')
245 |
246 | ## ADD STIG VIEWER COMMENT
247 | COMMENT = ET.Comment('DISA STIG Viewer :: 2.14')
248 | ROOT.append(COMMENT)
249 |
250 | ## ADD ASSET ELEMENT
251 | ASSET = ET.SubElement(ROOT, 'ASSET')
252 |
253 | ## GENERATE ASSET ELEMENTS
254 | ET.SubElement(ASSET, 'ROLE').text = CHECKLIST_INFO.get('ROLE') ## ASSET ROLE
255 | ET.SubElement(ASSET, 'ASSET_TYPE').text = CHECKLIST_INFO.get('ASSET_TYPE') ## ASSET TYPE
256 | ET.SubElement(ASSET, 'HOST_NAME').text = CHECKLIST_INFO.get('HOST_NAME') ## ASSET HOSTNAME
257 | ET.SubElement(ASSET, 'HOST_IP').text = CHECKLIST_INFO.get('HOST_IP') ## ASSET IP
258 | ET.SubElement(ASSET, 'HOST_MAC').text = CHECKLIST_INFO.get('HOST_MAC') ## ASSET MAC ADDRESS
259 | ET.SubElement(ASSET, 'HOST_FQDN').text = CHECKLIST_INFO.get('HOST_FQDN') ## ASSET FQDN
260 | ET.SubElement(ASSET, 'TARGET_COMMENT').text = CHECKLIST_INFO.get('TARGET_COMMENT') ## ASSET TARGET COMMENT
261 | ET.SubElement(ASSET, 'TECH_AREA').text = CHECKLIST_INFO.get('TECH_AREA') ## ASSET TECH AREA
262 | ET.SubElement(ASSET, 'TARGET_KEY').text = CHECKLIST_INFO.get('TARGET_KEY') ## ASSET TARGET KEY
263 | ET.SubElement(ASSET, 'WEB_OR_DATABASE').text = CHECKLIST_INFO.get('WEB_OR_DATABASE') ## ASSET WEB OR DATABASE
264 | ET.SubElement(ASSET, 'WEB_DB_SITE').text = CHECKLIST_INFO.get('WEB_DB_SITE') ## ASSET DB SITE
265 | ET.SubElement(ASSET, 'WEB_DB_INSTANCE').text = CHECKLIST_INFO.get('WEB_DB_INSTANCE') ## ASSET DB INSTANCE
266 |
267 | ## GENERATE STIG, ISTIG, STIG_INFO AND VULN ELEMENTS
268 | STIGS = ET.SubElement(ROOT, 'STIGS')
269 | ISTIG = ET.SubElement(STIGS, 'iSTIG')
270 | STIG_INFO = ET.SubElement(ISTIG, 'STIG_INFO')
271 |
272 | ## FORMAT STIG_ID FIELD
273 | STIG_ID = JSON_STIG['Title'].replace(' Security Technical Implementation Guide', '').replace(' ', '_').replace('.', '-').replace('/', '-')
274 |
275 | ## GENERATE STIG_INFO FIELDS
276 | ## TODO: CLASSIFICATION, CUSTOM NAME, FILENAME
277 | generate_stig_info(STIG_INFO, 'version', JSON_STIG['Version']) ## STIG VERSION
278 | generate_stig_info(STIG_INFO, 'classification', 'UNCLASSIFIED') ## STIG CLASSIFICATION
279 | generate_stig_info(STIG_INFO, 'customname', None) ## STIG CUSTOM NAME
280 | generate_stig_info(STIG_INFO, 'stigid', STIG_ID) ## STIG ID
281 | generate_stig_info(STIG_INFO, 'description', JSON_STIG['Description']) ## STIG DESCRIPTION
282 | generate_stig_info(STIG_INFO, 'filename', 'TEMP FILENAME') ## STIG FILENAME
283 | generate_stig_info(STIG_INFO, 'releaseinfo', JSON_STIG['ReleaseInfo']) ## STIG RELEASE INFO
284 | generate_stig_info(STIG_INFO, 'title', JSON_STIG['Title']) ## STIG TITLE
285 | generate_stig_info(STIG_INFO, 'uuid', str(uuid.uuid4())) ## STIG UUID
286 | generate_stig_info(STIG_INFO, 'notice', JSON_STIG['Notice']) ## STIG NOTICE
287 | generate_stig_info(STIG_INFO, 'source', JSON_STIG['Source']) ## STIG SOURCE
288 |
289 | ## GENERATE VULNERABILITIES
290 | for RULE in JSON_STIG['Rules']:
291 |
292 | ## CREATE VULN ELEMENT
293 | VULN = ET.SubElement(ISTIG, 'VULN')
294 |
295 | ## FORMAT RULE OUTPUTS
296 | STIG_REF = JSON_STIG['Title'] + " :: " + JSON_STIG['Version']
297 |
298 | ## CREATE RULES
299 | ## TODO: Group_Title, Check_Content_Ref, Weight, Classification, TargetKey, Legacy_ID
300 | generate_vuln(VULN, 'Vuln_Num', RULE['VulnID']) ## VULN ID
301 | generate_vuln(VULN, 'Severity', RULE['Severity']) ## VULN SEVERITY
302 | generate_vuln(VULN, 'Group_Title', 'SRG-APP-000001') ## VULNERABILITY GROUP TITLE
303 | generate_vuln(VULN, 'Rule_ID', RULE['RuleID']) ## VULNERABILITY RULE ID
304 | generate_vuln(VULN, 'Rule_Ver', RULE['StigID']) ## VULNERABILITY RULE VERSION
305 | generate_vuln(VULN, 'Rule_Title', RULE['RuleTitle']) ## VULNERABILITY RULE TITLE
306 | generate_vuln(VULN, 'Vuln_Discuss', RULE['VulnDiscussion']) ## VULNERABILITY DISCUSSION
307 | generate_vuln(VULN, 'IA_Controls', RULE['IAControls']) ## VULNERABILITY IA CONTROL
308 | generate_vuln(VULN, 'Check_Content', RULE['CheckText']) ## VULNERABILITY CHECK CONTENT
309 | generate_vuln(VULN, 'Fix_Text', RULE['FixText']) ## VULNERABILITY FIX TEXT
310 | generate_vuln(VULN, 'False_Positives', RULE['FalsePositives']) ## VULNERABILITY FALSE POSITIVIES
311 | generate_vuln(VULN, 'False_Negatives', RULE['FalseNegatives']) ## VULNERABILITY FALSE NEGATIVES
312 | generate_vuln(VULN, 'Documentable', RULE['Documentable']) ## VULNERABILITY DOCUMENTABLE
313 | generate_vuln(VULN, 'Mitigations', RULE['Mitigations']) ## VULNERABILITY MITIGATION
314 | generate_vuln(VULN, 'Potential_Impact', RULE['PotentialImpacts']) ## VULNERABILITY POTENTIAL IMPACT
315 | generate_vuln(VULN, 'Third_Party_Tools', RULE['ThirdPartyTools']) ## VULNERABILITY THIRD PARTY TOOLS
316 | generate_vuln(VULN, 'Mitigation_Control', RULE['MitigationControl']) ## VULNERABILITY MITIGATION CONTROLS
317 | generate_vuln(VULN, 'Responsibility', RULE['Responsibility']) ## VULNERABILITY RESPONSIBILITY
318 | generate_vuln(VULN, 'Security_Override_Guidance', RULE['SeverityOverrideGuidance']) ## VULNERABILITY SECURITY OVERRIDE GUIDE
319 | generate_vuln(VULN, 'Check_Content_Ref', 'M') ## VULNERABILITY CHECK CONTENT REFERENCE
320 | generate_vuln(VULN, 'Weight', '10.0') ## VULNERABILITY WEIGHT
321 | generate_vuln(VULN, 'Class', 'Unclass') ## VULNERABILITY CLASSIFICATION
322 | generate_vuln(VULN, 'STIGRef', STIG_REF) ## VULNERABILITY STIG REFERENCE
323 | generate_vuln(VULN, 'TargetKey', '3425') ## VULNERABILITY TARGET KEY
324 | generate_vuln(VULN, 'STIG_UUID', str(STIG_UUID)) ## VULNERABILITY UUID
325 | generate_vuln(VULN, 'LEGACY_ID', '') ## VULNERABILITY LEGACY ID
326 | generate_vuln(VULN, 'CCI_REF', RULE['CCI']) ## VULNERABILITY CCI REFERENCE
327 |
328 | ## RULE STATUS ELEMENTS
329 | ET.SubElement(VULN, "STATUS").text = "Not_Reviewed" ## VULNERABILITY FINDING STATUS
330 | ET.SubElement(VULN, 'FINDING_DETAILS').text = "" ## VULNERABILITY FINDING DETAILS
331 | ET.SubElement(VULN, 'COMMENTS').text = "" ## VULNERABILITY COMMENTS
332 | ET.SubElement(VULN, 'SEVERITY_OVERRIDE').text = "" ## VULNERABILITY SEVERITY OVERRIDE
333 | ET.SubElement(VULN, 'SEVERITY_JUSTIFICATION').text = "" ## VULNERABILITY SEVERITY JUSTIFICATION
334 |
335 | ## FORMAT EXPORT
336 | OUTPUT = ET.tostring(ROOT, encoding='UTF-8', xml_declaration=True, short_empty_elements=False)
337 |
338 | ## RETURN CHECKLIST
339 | return OUTPUT
340 |
341 | ## FUNCTION: GENERATE CHECKLIST FILE (CKL)
342 | def generate_ckl_file(CKL, FILENAME):
343 | ## SAVE XML OBJECT TO FILE
344 | with open(FILENAME, "wb") as FILE:
345 | FILE.write(CKL)
--------------------------------------------------------------------------------
/src/stig_parser/version.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 |
4 | def string():
5 | try:
6 | with open(os.path.dirname(__file__) + "/VERSION", "r", encoding="utf-8") as fh:
7 | version = fh.read().strip()
8 | if version:
9 | return version
10 | except:
11 | pass
12 | return "unknown (git checkout)"
--------------------------------------------------------------------------------
/src/stig_parser/xccdf-1.1.4.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 | This schema defines the eXtensible Configuration Checklist
17 | Description Format (XCCDF), a data format for defining
18 | security benchmarks and checklists, and for recording
19 | the results of applying such benchmarks.
20 | For more information, consult the specification
21 | document, "Specification for the Extensible Configuration
22 | Checklist Description Format", version 1.1 revision 4.
23 |
24 | This schema was developed by Neal Ziring, with ideas and
25 | assistance from David Waltermire. The following helpful
26 | individuals also contributed ideas to the definition
27 | of this schema: David Proulx, Andrew Buttner, Ryan Wilson,
28 | Matthew Kerr, Stephen Quinn. Ian Crawford found numerous
29 | discrepancies between this schema and the spec document.
30 | Peter Mell and his colleagues also made many suggestions.
31 | 1.1.4.3
32 |
33 |
34 | XCCDF Language
35 | Neal Ziring
36 | 1.1.4.3
37 | 2007-10-10
38 |
39 |
40 |
41 |
42 |
44 |
45 |
46 | Import the XML namespace because this schema uses
47 | the xml:lang and xml:base attributes.
48 |
49 |
50 |
51 |
52 |
53 |
55 |
56 |
57 | Import the simple Dublin Core namespace because this
58 | schema uses it for benchmark metadata and for references.
59 |
60 |
61 |
62 |
63 |
64 |
66 |
67 |
68 | Import the CIS platform schema, which we use for
69 | describing target IT platforms in the Benchmark. The
70 | CIS platform schema was designed by David Waltermire.
71 | Use of the CIS platform schema in XCCDF benchmarks is
72 | deprecated. The CIS platform schema is included only for
73 | backward compatibility with XCCDF 1.0. Use CPE 2.0 instead.
74 |
75 |
76 |
77 |
78 |
79 |
81 |
82 |
83 | Import the XCCDF-P platform schema, which we use
84 | for describing target IT platforms in the Benchmark.
85 | The CIS platform schema was designed by Neal Ziring
86 | using ideas and concepts developed by DISA, CIS, and
87 | others. Use of XCCDF-P platform specification in
88 | XCCDF benchmarks is deprecated. XCCDF-P is included
89 | in this schema only for backward compatibility with
90 | version 1.1 and 1.1.2. Use CPE 2.0 instead.
91 |
92 |
93 |
94 |
95 |
96 |
98 |
99 |
100 | Import the Common Platform Enumeration XML schema,
101 | which can be used for naming and describing target
102 | IT platforms in the Benchmark. Every CPE name is
103 | a URI that begins with "cpe:". For more details see
104 | "Common Platform Enumeration (CPE) - Specification",
105 | by Buttner, Wittbold, and Ziring (2007). Use of CPE 1.0
106 | definitions in XCCDF benchmarks is deprecated. CPE 1.0
107 | is included in this schema only for backward compatibility
108 | with XCCDF 1.1.2 and 1.1.3. CPE 2.0 Names should be used
109 | for simple (unitary) platforms, and CPE 2.0 Language
110 | definitions used for complex platforms.
111 |
112 |
113 |
114 |
115 |
116 |
118 |
119 |
120 | Import the Common Platform Enumeration language schema,
121 | which can be used for defining compound CPE tests for
122 | complex IT platforms in the Benchmark. For more info
123 | see "Common Platform Enumeration (CPE) - Specification",
124 | Version 2.0" by Buttner and Ziring (Sept 2007).
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | The benchmark tag is the top level element representing a
136 | complete security checklist, including descriptive text,
137 | metadata, test items, and test results. A Benchmark may
138 | be signed with a XML-Signature.
139 |
140 |
141 |
142 |
143 |
145 |
147 |
149 |
151 |
153 |
155 |
157 |
159 |
161 |
162 |
163 |
165 |
166 |
168 |
169 |
171 |
172 |
174 |
175 |
177 |
179 |
181 |
183 |
185 |
187 |
188 |
189 |
190 |
191 |
193 |
195 |
196 |
197 |
198 |
199 |
201 |
203 |
205 |
206 |
207 |
208 |
209 |
210 | Legal notices must have unique id values.
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | Items must have unique id values, and also they
219 | must not collide
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 | Model system attributes must be unique.
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 | Value item ids are special keys, need this for
236 | the valueIdKeyRef and valueExtIdKeyRef keyrefs below.
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 | Group item ids are special keys, need this for
245 | the groupIdKeyRef keyref below.
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 | Rule items have a unique key, we need
254 | this for the ruleIdKeyRef keyref below.
255 | (Rule key refs are used by rule-results.)
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 | Group and Rule item ids are special keys, we
264 | need this for the requiresIdKeyRef keyref below.
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 | Plaintext objects and Value objects each have
273 | and id, and they must be unique and not overlap.
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 | Profile objects have a unique id, it is used
282 | for extension, too.
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 | An extends attribute on Value object
291 | must reference an existing Value.
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 | An extends attribute on Group object
300 | must reference an existing Group.
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 | An extends attribute on Rule object
309 | must reference an existing Rule.
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 | An extends attribute on Profile object
318 | must reference an existing Profile.
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 | Check-export elements must reference existing values.
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 | Sub elements must reference existing Value or
335 | plain-text ids.
336 |
337 |
338 |
339 |
340 |
341 |
343 |
344 | The rule-result element idref must refer to an
345 | existing Rule.
346 |
347 |
348 |
349 |
350 |
351 |
353 |
354 | The requires a profile element in a TestResult
355 | element to refer to an existing Profile
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 | Data type for legal notice element that has text
367 | content and a unique id attribute.
368 |
369 |
370 |
371 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 | Data type for a reusable text block, with an
384 | unique id attribute.
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 | Data type for a reference citation, an href URL attribute
398 | (optional), with content of text or simple Dublin Core
399 | elements. Elements of this type can also have an override
400 | attribute to help manage inheritance.
401 |
402 |
403 |
404 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 | XML-Signature over the Benchmark; note that this will
416 | always be an 'enveloped' signature, so the single
417 | element child of this element should be dsig:Signature.
418 |
419 |
420 |
421 |
423 |
424 |
425 |
426 |
427 |
428 |
429 | Metadata for the Benchmark, should be Dublin Core
430 | or some other well-specified and accepted metadata
431 | format. If Dublin Core, then it will be a sequence
432 | of simple Dublin Core elements. The NIST checklist
433 | metadata should also be supported, although the
434 | specification document is still in draft in NIST
435 | special pub 800-70.
436 |
437 |
438 |
439 |
440 |
442 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 | The acceptance status of an Item with an optional date attribute
456 | that signifies the date of the status change.
457 |
458 |
459 |
460 |
461 |
462 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 | A suggested scoring model for a Benchmark, also
473 | encapsulating any parameters needed by the model.
474 | Every model is designated with a URI, which
475 | appears here as the system attribute.
476 |
477 |
478 |
479 |
480 |
482 |
483 |
485 |
486 |
487 |
488 |
489 | Parameter names must be unique.
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 | Type for a scoring model parameter: a name and a
500 | string value.
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 | The possible status codes for an Benchmark or Item to be
515 | inherited from the parent element if it is not defined.
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 | Type for a version number, with a timestamp attribute
531 | for when the version was made.
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 | Type for a string with an xml:lang attribute.
549 | Elements of this type can also have an override
550 | attribute to help manage inheritance.
551 |
552 |
553 |
554 |
555 |
556 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 | Type for a string with XHTML elements and xml:lang attribute.
566 | Elements of this type can also have an override
567 | attribute to help manage inheritance.
568 |
569 |
570 |
571 |
574 |
575 |
576 |
578 |
579 |
580 |
581 |
582 |
583 | Type for a string with embedded Value substitutions and
584 | XHTML elements, and an xml:lang attribute. Elements of
585 | this type can also have an override attribute to help
586 | manage inheritance. [Note: this definition is rather
587 | loose, it allows anything whatsoever to occur insides
588 | XHTML tags inside here. Further, constraints of the XHTML
589 | schema do not get checked! It might be possible to solve
590 | this using XML Schema redefinition features.]
591 |
592 |
593 |
594 |
595 |
597 |
598 |
599 |
601 |
602 |
603 |
604 |
605 |
606 | Type for a string with embedded Value substitutions and
607 | XHTML elements, an xml:lang attribute, and a profile-note tag.
608 |
609 |
610 |
611 |
612 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 | Type for a string with embedded Value substitutions
623 | and XHTML elements, and an xml:lang attribute.
624 | Elements of this type can also have an override
625 | attribute to help manage inheritance.
626 |
627 |
628 |
629 |
631 |
632 |
633 |
635 |
636 |
637 |
638 |
639 |
640 | Data type for elements that have no content,
641 | just a mandatory id reference.
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 | Data type for elements that have no content,
651 | just a space-separated list of id references.
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 | Data type for elements that have no content,
661 | just a mandatory id reference, but also have
662 | an override attribute for controlling inheritance.
663 |
664 |
665 |
666 |
667 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 | Data type for elements that have no content,
677 | just a mandatory URI as an id. (This is mainly for the
678 | platform element, which uses CPE URIs and CPE Language
679 | identifers used as platform identifiers.) When referring
680 | to a local CPE Language identifier, the URL should use
681 | local reference syntax: "#cpeid1".
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 | Data type for elements that have no content,
691 | just a mandatory URI reference, but also have
692 | an override attribute for controlling inheritance.
693 |
694 |
695 |
696 |
697 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 | Type element type imposes constraints shared by all
711 | Groups, Rules and Values. The itemType is abstract, so
712 | the element Item can never appear in a valid XCCDF document.
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 | This abstract item type represents the basic data shared by all
721 | Groups, Rules and Values
722 |
723 |
724 |
725 |
727 |
729 |
731 |
733 |
735 |
737 |
739 |
740 |
741 |
743 |
745 |
746 |
748 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 | This abstract item type represents the basic data shared by all
763 | Groups and Rules. It extends the itemType given above.
764 |
765 |
766 |
767 |
768 |
769 |
772 |
775 |
777 |
779 |
780 |
782 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 | Data type for the Group element that represents a grouping of
797 | Groups, Rules and Values.
798 |
799 |
800 |
801 |
802 |
803 |
805 |
806 |
807 |
808 |
809 |
811 |
812 |
813 |
814 |
815 |
816 |
817 |
818 |
819 |
820 |
821 |
822 |
823 | The Rule element contains the description for
824 | a single item of guidance or constraint. Rules
825 | form the basis for testing a target platform for
826 | compliance with a benchmark, for scoring, and
827 | for conveying descriptive prose, identifiers,
828 | references, and remediation information.
829 |
830 |
831 |
832 |
833 |
834 |
835 |
836 |
837 |
838 |
839 |
840 |
841 |
842 |
843 |
844 |
845 | Data type for the Rule element that represents a
846 | specific benchmark test.
847 |
848 |
849 |
850 |
851 |
852 |
854 |
856 |
859 |
861 |
863 |
864 |
866 |
868 |
869 |
871 |
872 |
874 |
876 |
878 |
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 | Type for a long-term globally meaningful identifier,
890 | consisting of a string (ID) and a URI of the naming
891 | scheme within which the name is meaningful.
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 | Data type for the warning element under the Rule
905 | object, a rich text string with substitutions
906 | allowed, plus an attribute for the kind of warning.
907 |
908 |
909 |
910 |
911 |
914 |
915 |
916 |
917 |
918 |
919 |
920 |
921 | Allowed warning category keywords for the warning
922 | element. The allowed categories are:
923 | general=broad or general-purpose warning (default
924 | for compatibility for XCCDF 1.0)
925 | functionality=warning about possible impacts to
926 | functionality or operational features
927 | performance=warning about changes to target
928 | system performance or throughput
929 | hardware=warning about hardware restrictions or
930 | possible impacts to hardware
931 | legal=warning about legal implications
932 | regulatory=warning about regulatory obligations
933 | or compliance implications
934 | management=warning about impacts to the mgmt
935 | or administration of the target system
936 | audit=warning about impacts to audit or logging
937 | dependency=warning about dependencies between
938 | this Rule and other parts of the target
939 | system, or version dependencies.
940 |
941 |
942 |
943 |
944 |
945 |
946 |
947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 |
956 |
957 |
958 |
959 | Data type for the fixText element that represents
960 | a rich text string, with substitutions allowed, and
961 | a series of attributes that qualify the fix.
962 |
963 |
964 |
965 |
966 |
968 |
970 |
972 |
974 |
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 | Type for a string with embedded Value and instance
984 | substitutions and an optional platform id ref attribute, but
985 | no embedded XHTML markup.
986 | The platform attribute should refer to a platform-definition
987 | element in the platform-definitions child of the Benchmark.
988 |
989 |
990 |
991 |
992 |
993 |
994 |
995 |
997 |
999 |
1001 |
1003 |
1004 |
1005 |
1006 |
1007 |
1008 |
1009 |
1010 | Allowed strategy keyword values for a Rule fix or
1011 | fixtext. The allowed values are:
1012 | unknown= strategy not defined (default for forward
1013 | compatibility for XCCDF 1.0)
1014 | configure=adjust target config or settings
1015 | patch=apply a patch, hotfix, or update
1016 | policy=remediation by changing policies/procedures
1017 | disable=turn off or deinstall something
1018 | enable=turn on or install something
1019 | restrict=adjust permissions or ACLs
1020 | update=install upgrade or update the system
1021 | combination=combo of two or more of the above
1022 |
1023 |
1024 |
1025 |
1026 |
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 |
1035 |
1036 |
1037 |
1038 |
1039 |
1040 | Allowed rating values values for a Rule fix
1041 | or fixtext: disruption, complexity, and maybe overhead.
1042 | The possible values are:
1043 | unknown= rating unknown or impossible to estimate
1044 | (default for forward compatibility for XCCDF 1.0)
1045 | low = little or no potential for disruption,
1046 | very modest complexity
1047 | medium= some chance of minor disruption,
1048 | substantial complexity
1049 | high = likely to cause serious disruption, very complex
1050 |
1051 |
1052 |
1053 |
1054 |
1055 |
1056 |
1057 |
1058 |
1059 |
1060 |
1061 |
1062 |
1063 | Type for an instance element in a fix element. The
1064 | instance element inside a fix element designates a
1065 | spot where the name of the instance should be
1066 | substituted into the fix template to generate the
1067 | final fix data. The instance element in this usage
1068 | has one optional attribute: context.
1069 |
1070 |
1071 |
1073 |
1074 |
1075 |
1076 |
1077 |
1078 | The type for an element that can contains a boolean
1079 | expression based on checks. This element can have only
1080 | complex-check and check elements as children. It has two
1081 | attributes: operator and negate. The operator attribute
1082 | can have values "OR" or "AND", and the negate attribute is
1083 | boolean. See the specification document for truth tables
1084 | for the operators and negations. Note: complex-check is
1085 | defined in this way for conceptual equivalence with OVAL.
1086 |
1087 |
1088 |
1089 |
1090 |
1091 |
1092 |
1094 |
1096 |
1097 |
1098 |
1099 |
1100 |
1101 | The type for the allowed operator names for the
1102 | complex-check operator attribute. For now, we just
1103 | allow boolean AND and OR as operators. (The
1104 | complex-check has a separate mechanism for negation.)
1105 |
1106 |
1107 |
1108 |
1109 |
1110 |
1111 |
1112 |
1113 |
1114 |
1115 |
1116 | Data type for the check element, a checking system
1117 | specification URI, and XML content. The content of the
1118 | check element is: zero or more check-export elements,
1119 | zero or more check-content-ref elements, and finally
1120 | an optional check-content element. An content-less
1121 | check element isn't legal, but XSD cannot express that!
1122 |
1123 |
1124 |
1125 |
1127 |
1129 |
1132 |
1135 |
1136 |
1137 |
1138 |
1140 |
1141 |
1142 |
1143 |
1144 |
1145 |
1146 | Data type for the check-import element, which specifies a
1147 | value that the benchmark author wishes to retrieve from the
1148 | the checking system. The import-name attribute gives the
1149 | name or id of the value in the checking system. When the
1150 | check-import element appears in the context of a rule-result,
1151 | then the element's content is the desired value. When the
1152 | check-import element appears in the context of a Rule, then
1153 | it should be empty and any content must be ignored.
1154 |
1155 |
1156 |
1157 |
1158 |
1160 |
1161 |
1162 |
1163 |
1164 |
1165 |
1166 |
1167 | Data type for the check-export element, which specifies
1168 | a mapping between an XCCDF internal Value id and a
1169 | value name to be used by the checking system or processor.
1170 |
1171 |
1172 |
1173 |
1174 |
1175 |
1176 |
1177 |
1178 |
1179 | Data type for the check-content-ref element, which
1180 | points to the code for a detached check in another file.
1181 | This element has no body, just a couple of attributes:
1182 | href and name. The name is optional, if it does not appear
1183 | then this reference is to the entire other document.
1184 |
1185 |
1186 |
1187 |
1188 |
1189 |
1190 |
1191 |
1192 |
1193 | Data type for the check-content element, which holds
1194 | the actual code of an enveloped check in some other
1195 | (non-XCCDF) language. This element can hold almost
1196 | anything; XCCDF tools do not process its content directly.
1197 |
1198 |
1199 |
1200 |
1201 |
1202 |
1203 |
1204 |
1205 |
1206 |
1207 | Data type for a Rule's weight, a non-negative real number.
1208 |
1209 |
1210 |
1211 |
1212 |
1213 |
1214 |
1215 |
1216 |
1217 |
1218 |
1219 |
1220 |
1221 |
1222 |
1223 |
1224 |
1225 |
1226 |
1227 |
1228 |
1229 |
1230 |
1231 |
1232 |
1233 | Data type for the Value element, which represents a
1234 | tailorable string, boolean, or number in the Benchmark.
1235 |
1236 |
1237 |
1238 |
1239 |
1240 |
1242 |
1244 |
1246 |
1248 |
1250 |
1252 |
1254 |
1256 |
1257 |
1259 |
1261 |
1263 |
1265 |
1266 |
1267 |
1268 |
1269 |
1270 |
1271 |
1272 |
1273 |
1274 |
1275 |
1276 | The choice element specifies a list of legal or suggested
1277 | choices for a Value object. It holds one or more choice
1278 | elements, a mustMatch attribute, and a selector attribute.
1279 |
1280 |
1281 |
1282 |
1284 |
1285 |
1286 |
1288 |
1289 |
1290 |
1291 |
1292 |
1293 | This type is for an element that has string content
1294 | and a selector attribute. It is used for some of
1295 | the child elements of Value.
1296 |
1297 |
1298 |
1299 |
1300 |
1302 |
1303 |
1304 |
1305 |
1306 |
1307 |
1308 |
1309 | This type is for an element that has numeric content
1310 | and a selector attribute. It is used for two of
1311 | the child elements of Value.
1312 |
1313 |
1314 |
1315 |
1316 |
1318 |
1319 |
1320 |
1321 |
1322 |
1323 |
1324 |
1325 | Data type for elements that have no content, just a URI.
1326 |
1327 |
1328 |
1329 |
1330 |
1331 |
1332 |
1333 |
1334 | Allowed data types for Values, just string, numeric,
1335 | and true/false.
1336 |
1337 |
1338 |
1339 |
1340 |
1341 |
1342 |
1343 |
1344 |
1345 |
1346 |
1347 |
1348 | Allowed operators for Values. Note that most of
1349 | these are valid only for numeric data, but the
1350 | schema doesn't enforce that.
1351 |
1352 |
1353 |
1354 |
1355 |
1356 |
1357 |
1358 |
1359 |
1360 |
1361 |
1362 |
1363 |
1364 |
1365 |
1366 |
1367 | Allowed interface hint values. When an interfaceHint
1368 | appears on the Value, it provides a suggestion to a
1369 | tailoring or benchmarking tool about how to present the
1370 | UI for adjusting a Value.
1371 |
1372 |
1373 |
1374 |
1375 |
1376 |
1377 |
1378 |
1379 |
1380 |
1381 |
1382 |
1383 |
1384 |
1385 |
1386 |
1387 |
1388 |
1389 |
1390 |
1391 |
1392 |
1393 |
1394 |
1395 |
1396 |
1397 |
1398 |
1399 |
1400 |
1401 |
1402 |
1403 |
1404 |
1405 |
1406 |
1407 |
1408 | Data type for the Profile element, which holds a
1409 | specific tailoring of the Benchmark. The main part
1410 | of a Profile is the selectors: select, set-value,
1411 | refine-rule, and refine-value. A Profile may also be
1412 | signed with an XML-Signature.
1413 |
1414 |
1415 |
1416 |
1418 |
1420 |
1422 |
1424 |
1426 |
1428 |
1429 |
1431 |
1433 |
1435 |
1437 |
1438 |
1440 |
1441 |
1442 |
1444 |
1446 |
1448 |
1449 |
1450 |
1451 |
1452 |
1453 |
1454 |
1455 |
1456 |
1457 |
1458 |
1459 | Type for the select element in a Profile; all it has are two
1460 | attributes, no content. The two attributes are idref which
1461 | refers to a Group or Rule, and selected which is boolean.
1462 | As content, the select element can contain zero or more
1463 | remark elements, which allows the benchmark author to
1464 | add explanatory material or other additional prose.
1465 |
1466 |
1467 |
1468 |
1470 |
1471 |
1472 |
1473 |
1474 |
1475 |
1476 |
1477 |
1478 | Type for the set-value element in a Profile; it
1479 | has one required attribute and string content. The
1480 | attribute is 'idref', it refers to a Value.
1481 |
1482 |
1483 |
1484 |
1485 |
1486 |
1487 |
1488 |
1489 |
1490 |
1491 |
1492 |
1493 | Type for the refine-value element in a Profile; all it has
1494 | are three attributes, no content. The three attributes are
1495 | 'idref' which refers to a Value, 'selector' which designates
1496 | certain element children of the Value, and 'operator' which
1497 | can override the operator attribute of the Value.
1498 | As content, the refine-value element can contain zero or more
1499 | remark elements, which allows the benchmark author to
1500 | add explanatory material or other additional prose.
1501 |
1502 |
1503 |
1504 |
1506 |
1507 |
1509 |
1511 |
1513 |
1514 |
1515 |
1516 |
1517 |
1518 | Type for the refine-rule element in a Profile; all it has
1519 | are four attributes, no content. The main attribute is
1520 | 'idref' which refers to a Rule, and three attributes that
1521 | allow the Profile author to adjust aspects of how a Rule is
1522 | processed during a benchmark run: weight, severity, role.
1523 | As content, the refine-rule element can contain zero or more
1524 | remark elements, which allows the benchmark author to
1525 | add explanatory material or other additional prose.
1526 |
1527 |
1528 |
1529 |
1531 |
1532 |
1533 |
1534 |
1536 |
1538 |
1540 |
1541 |
1542 |
1543 |
1544 |
1545 |
1546 |
1547 |
1548 |
1549 |
1550 | Data type for the TestResult element, which holds the
1551 | results of one application of the Benchmark. The optional
1552 | test-system attribute gives the name of the benchmarking tool.
1553 |
1554 |
1555 |
1556 |
1557 |
1558 |
1560 |
1561 |
1562 |
1564 |
1566 |
1568 |
1570 |
1572 |
1574 |
1576 |
1578 |
1580 |
1582 |
1584 |
1585 |
1586 |
1587 |
1588 |
1589 |
1590 |
1591 |
1592 |
1593 |
1594 |
1595 |
1597 |
1599 |
1600 |
1601 |
1602 |
1603 |
1604 |
1605 |
1606 |
1607 |
1608 |
1609 |
1610 |
1611 | Type for a score value in a TestResult, the content is a
1612 | real number and the element can have two optional attributes.
1613 |
1614 |
1615 |
1616 |
1617 |
1619 |
1621 |
1622 |
1623 |
1624 |
1625 |
1626 |
1627 |
1628 | This element holds a list of facts about the target system
1629 | or platform. Each fact is an element of type factType.
1630 | Each fact must have a name, but duplicate names are allowed.
1631 | (For example, if you had a fact about MAC addresses, and the
1632 | target system had three NICs, then you'd need three
1633 | instance of the "urn:xccdf:fact:ethernet:MAC" fact.)
1634 |
1635 |
1636 |
1637 |
1639 |
1640 |
1641 |
1642 |
1643 |
1644 |
1645 | Type for an identity element in a TestResult.
1646 | The content is a string, the name of the identity.
1647 | The authenticated attribute indicates whether the
1648 | test system authenticated using that identity in order
1649 | to apply the benchmark. The privileged attribute indicates
1650 | whether the identity has access rights beyond those of
1651 | normal system users (e.g. "root" on Unix")
1652 |
1653 |
1654 |
1655 |
1656 |
1658 |
1660 |
1661 |
1662 |
1663 |
1664 |
1665 |
1666 |
1667 | Element type for a fact about a target system: a
1668 | name-value pair with a type. The content of the element
1669 | is the value, the type attribute gives the type. This
1670 | is an area where XML schema is weak: we can't make the
1671 | schema validator check that the content matches the type.
1672 |
1673 |
1674 |
1675 |
1676 |
1678 |
1680 |
1681 |
1682 |
1683 |
1684 |
1685 |
1686 |
1687 | This element holds all the information about the
1688 | application of one rule to a target. It may only
1689 | appear as part of a TestResult object.
1690 |
1691 |
1692 |
1693 |
1695 |
1697 |
1699 |
1701 |
1703 |
1705 |
1706 |
1708 |
1709 |
1710 |
1712 |
1714 |
1715 |
1716 |
1718 |
1719 |
1720 |
1721 |
1722 |
1723 | Type for an instance element in a rule-result.
1724 | The content is a string, but the element may
1725 | also have two attribute: context and parentContext.
1726 | This type records the details of the target system
1727 | instance for multiply instantiated rules.
1728 |
1729 |
1730 |
1731 |
1732 |
1734 |
1736 |
1737 |
1738 |
1739 |
1740 |
1741 |
1742 |
1743 | Type for an override block in a rule-result.
1744 | It contains five mandatory parts: time, authority,
1745 | old-result, new-result, and remark.
1746 |
1747 |
1748 |
1749 |
1751 |
1753 |
1755 |
1756 |
1757 |
1758 |
1759 |
1760 |
1761 |
1762 |
1763 | Type for a message generated by the checking
1764 | engine or XCCDF tool during benchmark testing.
1765 | Content is string plus required severity attribute.
1766 |
1767 |
1768 |
1769 |
1770 |
1772 |
1773 |
1774 |
1775 |
1776 |
1777 |
1778 |
1779 | Allowed values for message severity.
1780 |
1781 |
1782 |
1783 |
1784 |
1785 |
1786 |
1787 |
1788 |
1789 |
1790 |
1791 |
1792 | Allowed result indicators for a test, several possibilities:
1793 | pass= the test passed, target complies w/ benchmark
1794 | fail= the test failed, target does not comply
1795 | error= an error occurred and test could not complete,
1796 | or the test does not apply to this plaform
1797 | unknown= could not tell what happened, results
1798 | with this status are not to be scored
1799 | notapplicable=Rule did not apply to test target
1800 | fixed=rule failed, but was later fixed (score as pass)
1801 | notchecked=Rule did not cause any evaluation by
1802 | the checking engine (role of "unchecked")
1803 | notselected=Rule was not selected in the Benchmark,
1804 | and therefore was not checked (selected="0")
1805 | informational=Rule was evaluated by the checking
1806 | engine, but isn't to be scored (role of "unscored")
1807 |
1808 |
1809 |
1810 |
1811 |
1812 |
1813 |
1814 |
1815 |
1816 |
1817 |
1818 |
1819 |
1820 |
1821 |
1822 |
1823 |
1824 |
1825 | Allowed severity values for a Rule.
1826 | there are several possible values:
1827 | unknown= severity not defined (default, for forward
1828 | compatibility from XCCDF 1.0)
1829 | info = rule is informational only, failing the
1830 | rule does not imply failure to conform to
1831 | the security guidance of the benchmark.
1832 | (usually would also have a weight of 0)
1833 | low = not a serious problem
1834 | medium= fairly serious problem
1835 | high = a grave or critical problem
1836 |
1837 |
1838 |
1839 |
1840 |
1841 |
1842 |
1843 |
1844 |
1845 |
1846 |
1847 |
1848 |
1849 |
1850 | Allowed checking and scoring roles for a Rule.
1851 | There are several possible values:
1852 | full = if the rule is selected, then check it and let the
1853 | result contribute to the score and appear in reports
1854 | (default, for compatibility for XCCDF 1.0).
1855 | unscored = check the rule, and include the results in
1856 | any report, but do not include the result in
1857 | score computations (in the default scoring model
1858 | the same effect can be achieved with weight=0)
1859 | unchecked = don't check the rule, just force the result
1860 | status to 'unknown'. Include the rule's
1861 | information in any reports.
1862 |
1863 |
1864 |
1865 |
1866 |
1867 |
1868 |
1869 |
1870 |
1871 |
1872 |
1873 |
2012 |
2013 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkeech/stig_parser/e8b5fd92e382148c4f806530f36878fa1723dc1e/tests/__init__.py
--------------------------------------------------------------------------------
/tests/resources/U_Docker_Enterprise_2-x_Linux-UNIX_V1R1_STIG.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkeech/stig_parser/e8b5fd92e382148c4f806530f36878fa1723dc1e/tests/resources/U_Docker_Enterprise_2-x_Linux-UNIX_V1R1_STIG.zip
--------------------------------------------------------------------------------
/tests/stig_parser_test.py:
--------------------------------------------------------------------------------
1 | ## ===================================
2 | ## ===== STIG PARSER TEST SCRIPT =====
3 | ## ===================================
4 |
5 | ## Created By : Peter Keech
6 | ## Email : peter.a.keech@gmail.com
7 | ## Version : 1.0.2
8 | ## Description : PyTest file for STIG-Parser v1.0.2
9 | ## Requirements: PyTest, XmltoDict
10 | ## Testing : docker run -it --rm -v $(PWD):/stig-parser python /bin/bash
11 | ## cd stig-parser && pip install pytest xmltodict
12 | ## pytest -s
13 |
14 | ## IMPORT REQUIRED MODULES
15 | import zipfile, os, json, xmltodict, pytest
16 |
17 | ## IMPORT STIG-PARSER
18 | import src.stig_parser as stig_parser
19 |
20 | ## ----------------------------------------
21 | ## ---- STATICALLY SET TEST VARIABLES -----
22 | ## ----------------------------------------
23 |
24 | ## STIG FILENAME
25 | FILENAME = "tests/resources/U_Docker_Enterprise_2-x_Linux-UNIX_V1R1_STIG.zip"
26 |
27 | ## EXPORT PATHS
28 | EXPORT_PATH_JSON = './tests/pytest-stig.json'
29 | EXPORT_PATH_CKL = './tests/pytest-stig.ckl'
30 |
31 | ## CHECKLIST METADATA
32 | CHECKLIST_INFO ={
33 | "ROLE": "None",
34 | "ASSET_TYPE": "Computing",
35 | "HOST_NAME": "Test_Host",
36 | "HOST_IP": "1.2.3.4",
37 | "HOST_MAC": "",
38 | "HOST_FQDN": "test.hostname.dev",
39 | "TARGET_COMMENT": "",
40 | "TECH_AREA": "",
41 | "TARGET_KEY": "3425",
42 | "WEB_OR_DATABASE": "false",
43 | "WEB_DB_SITE": "",
44 | "WEB_DB_INSTANCE": ""
45 | }
46 |
47 | ## -----------------------------
48 | ## ----- PRIVATE FUNCTIONS -----
49 | ## -----------------------------
50 |
51 | ## FUNCTION: CONVERT CHECKLIST (XML) TO DICTIONARY
52 | def convert_ckl_to_dict(RAW_CKL):
53 | ## CONVERT XML TO PYTHON DICTIONARY
54 | CHECKLIST_DICT = xmltodict.parse(RAW_CKL, dict_constructor=dict)
55 |
56 | ## RETURN DICTIONARY
57 | return CHECKLIST_DICT
58 |
59 | ## -----------------
60 | ## ----- TESTS -----
61 | ## -----------------
62 |
63 | ## TEST: ENSURE STIG FILE EXISTS
64 | ## REQUIRES: N/A
65 | def test_requirements() -> None:
66 | ## CHECK FILE EXISTS
67 | assert os.path.isfile(FILENAME), 'File does not exist: %s' % FILENAME
68 |
69 | ## TEST: ATTEMPT TO PARSE STIG FILE
70 | ## REQUIRES: STIG (XML)
71 | def test_convert_xccdf() -> None:
72 | ## OPEN XML FILE FROM ZIP FILE AND OBTAIN LIST OF FILES
73 | z = zipfile.ZipFile(FILENAME)
74 | files = z.namelist()
75 |
76 | ## FIND MANUAL STIG FILE
77 | for file in files:
78 | if file.endswith('_Manual-xccdf.xml'):
79 | ## HANDLE MACOS
80 | if not file.startswith('__MACOS'):
81 | ## DETERMINE FILE NAME
82 | STIG_FILENAME = file
83 | break
84 |
85 | ## ENSURE FILE IS FOUND
86 | assert STIG_FILENAME is not None, 'Manual STIG File was NOT FOUND'
87 |
88 | ## READ STIG FILE
89 | RAW_FILE = z.read(STIG_FILENAME)
90 |
91 | ## ENSURE FILE READ CORRECTLY
92 | assert RAW_FILE is not None, 'Unable to Read STIG File (%s)' % STIG_FILENAME
93 |
94 | ## CONVERT RAW STIG TO JSON OBJECT
95 | STIG_JSON = stig_parser.convert_xccdf(RAW_FILE)
96 |
97 | ## ENSURE STIG WAS PARSED
98 | assert STIG_JSON is not None, 'Unable to Parse STIG File (%s)' % STIG_FILENAME
99 |
100 | ## VALIDATE KNOWN ENTRIES
101 | assert STIG_JSON['Title'] == "Docker Enterprise 2.x Linux/UNIX Security Technical Implementation Guide", "STIG Title Parsed Incorrectly" ## STIG TITLE
102 | assert STIG_JSON['BenchmarkDate'] == "19 Jul 2019" ## BENCHMARK DATE
103 | assert STIG_JSON['Rules'][0]['VulnID'] == "V-94863" ## FIRST RULE ID
104 | assert STIG_JSON['ReleaseInfo'] == "Release: 1 Benchmark Date: 19 Jul 2019" ## RELEASE INFO
105 | assert STIG_JSON['Source'] == "STIG.DOD.MIL" ## SOURCE
106 | assert STIG_JSON['Notice'] == "terms-of-use" ## NOTICE
107 | assert STIG_JSON['Rules'][0]['CCI'] == "CCI-000054" ## FIRST RULE CCI NUMBER
108 | assert STIG_JSON['Rules'][0]['StigID'] == "DKER-EE-001000" ## FIRST RULE STIG ID
109 | assert STIG_JSON['Rules'][0]['RuleID'] == "SV-104693r1_rule" ## FIRST RULE ID
110 |
111 | ## TEST: ATTEMPT TO PARSE STIG FILE W/O EXTRACTING FILE
112 | ## REQUIRES: STIG (ZIP)
113 | def test_convert_stig() -> None:
114 | ## CONVERT STIG TO JSON OBJECT
115 | STIG_JSON = stig_parser.convert_stig(FILENAME)
116 |
117 | ## ENSURE STIG WAS PARSED
118 | assert STIG_JSON is not None, 'Unable to Parse STIG File (%s)' % FILENAME
119 |
120 | ## VALIDATE KNOWN ENTRIES
121 | assert STIG_JSON['Title'] == "Docker Enterprise 2.x Linux/UNIX Security Technical Implementation Guide", "STIG Title Parsed Incorrectly" ## STIG TITLE
122 | assert STIG_JSON['BenchmarkDate'] == "19 Jul 2019" ## BENCHMARK DATE
123 | assert STIG_JSON['Rules'][0]['VulnID'] == "V-94863" ## FIRST RULE ID
124 | assert STIG_JSON['ReleaseInfo'] == "Release: 1 Benchmark Date: 19 Jul 2019" ## RELEASE INFO
125 | assert STIG_JSON['Source'] == "STIG.DOD.MIL" ## SOURCE
126 | assert STIG_JSON['Notice'] == "terms-of-use" ## NOTICE
127 | assert STIG_JSON['Rules'][0]['CCI'] == "CCI-000054" ## FIRST RULE CCI NUMBER
128 | assert STIG_JSON['Rules'][0]['StigID'] == "DKER-EE-001000" ## FIRST RULE STIG ID
129 | assert STIG_JSON['Rules'][0]['RuleID'] == "SV-104693r1_rule" ## FIRST RULE ID
130 |
131 | ## TEST: EXTRACT STIG FROM ZIP
132 | ## REQUIRES: STIG (ZIP)
133 | def test_extract_stig() -> None:
134 | ## CONVERT STIG TO JSON OBJECT
135 | STIG_JSON = stig_parser.convert_stig(FILENAME)
136 |
137 | ## ENSURE STIG WAS PARSED
138 | assert STIG_JSON is not None, 'Unable to Parse STIG File (%s)' % FILENAME
139 |
140 | ## TEST: ATTEMPT TO SAVE STIG TO JSON FILE
141 | ## REQUIRES: STIG (ZIP), EXPORT FILE PATH
142 | def test_generate_stig_json() -> None:
143 | ## CONVERT STIG TO JSON OBJECT
144 | STIG_JSON = stig_parser.convert_stig(FILENAME)
145 |
146 | ## CREATE JSON FILE
147 | stig_parser.generate_stig_json(STIG_JSON, EXPORT_PATH_JSON)
148 |
149 | ## ENSURE FILE CREATION WAS SUCCESSFUL
150 | assert os.path.exists(EXPORT_PATH_JSON), "Exported File (%s) Doesn't Exist!" % EXPORT_PATH_JSON
151 |
152 | ## ATTEMPT TO READ JSON FILE TO ENSURE VALID EXPORT
153 | FILE = open(EXPORT_PATH_JSON, "r")
154 | STIG = json.load(FILE)
155 |
156 | ## ENSURE FIELDS ARE READABLE
157 | assert STIG['Title'] == "Docker Enterprise 2.x Linux/UNIX Security Technical Implementation Guide", "Unable to read JSON File (%s)" % EXPORT_PATH_JSON
158 | assert STIG['Rules'][0]['VulnID'] == "V-94863", "Unable to read JSON File (%s)" % EXPORT_PATH_JSON
159 |
160 | ## DELETE TEST FILES
161 | os.remove(EXPORT_PATH_JSON)
162 |
163 | ## TEST: ATTEMPT TO GENERATE A BLANK CHECKLIST (CKL) FILE
164 | ## REQUIRES: STIG (ZIP), CHECKLIST INFO (JSON)
165 | def test_generate_ckl() -> None:
166 | ## ATTEMPT TO GENERATE CKL
167 | CKL = stig_parser.generate_ckl(FILENAME, CHECKLIST_INFO)
168 |
169 | ## VALIDATE RESPONSE RETURNED
170 | assert CKL is not None, 'Unable to generate CKL based upon the passed STIG File (%s)' % FILENAME
171 |
172 | ## CONVERT CHECKLIST (CKL) TO DICTIONARY
173 | CHECKLIST_DICT = convert_ckl_to_dict(CKL)
174 |
175 | ## VALIDATE CKL FIELDS (ASSET INFO)
176 | ASSET = CHECKLIST_DICT['CHECKLIST']['ASSET']
177 | assert ASSET['ROLE'] == "None", 'Checklist Asset Role is Incorrect. %s (From Function) =/= None' % ASSET['ROLE'] ## ASSET ROLE FIELD
178 |
179 | ## VALIDATE CKL FIELDS (STIG INFO)
180 | STIG = CHECKLIST_DICT['CHECKLIST']['STIGS']['iSTIG']['STIG_INFO']['SI_DATA']
181 | assert STIG[0]['SID_DATA'] == "1", 'STIG Version is Incorrect. %s (From Function) =/= 1' % STIG[0]['SID_DATA'] ## STIG VERSION FIELD
182 | assert STIG[3]['SID_DATA'] == "Docker_Enterprise_2-x_Linux-UNIX", 'STIG ID is Incorrect. %s (From Function) =/= Docker_Enterprise_2-x_Linux-UNIX_STIG' % STIG[3]['SID_DATA'] ## STIG ID
183 |
184 | ## TEST: GENERATE CKL FILE
185 | ## REQUIRES: CHECKLIST XML, OUTPUT FILENAME
186 | def test_generate_ckl_file() -> None:
187 | ## ATTEMPT TO GENERATE CKL
188 | CKL = stig_parser.generate_ckl(FILENAME, CHECKLIST_INFO)
189 |
190 | ## VALIDATE RESPONSE RETURNED
191 | assert CKL is not None, 'Unable to generate CKL based upon the passed STIG File (%s)' % FILENAME
192 |
193 | ## OUTPUT CKL TO FILE
194 | stig_parser.generate_ckl_file(CKL, EXPORT_PATH_CKL)
195 |
196 | ## ENSURE FILE WAS CREATED
197 | assert os.path.exists(EXPORT_PATH_CKL), "Exported File (%s) Doesn't Exist!" % EXPORT_PATH_CKL
198 |
199 | ## ATTEMPT TO LOAD FILE TO ENSURE A VALID EXPORT
200 | FILE = open(EXPORT_PATH_CKL, "r")
201 | CHECKLIST = xmltodict.parse(FILE.read(), dict_constructor=dict)
202 |
203 | ## ENSURE FIELDS ARE READABLE
204 | assert CHECKLIST['CHECKLIST']['ASSET']['ROLE'] == "None", "Unable to read CKL File (%s)" % EXPORT_PATH_CKL
205 |
206 | ## DELETE TEST FILES
207 | os.remove(EXPORT_PATH_CKL)
--------------------------------------------------------------------------------