check-opencloud-security 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. check_opencloud_security-1.0.1/.gitignore +247 -0
  2. check_opencloud_security-1.0.1/CHANGELOG.md +156 -0
  3. check_opencloud_security-1.0.1/LICENSE +674 -0
  4. check_opencloud_security-1.0.1/PKG-INFO +2375 -0
  5. check_opencloud_security-1.0.1/README.md +1688 -0
  6. check_opencloud_security-1.0.1/ansible/README.md +184 -0
  7. check_opencloud_security-1.0.1/check_opencloud_security.py +1442 -0
  8. check_opencloud_security-1.0.1/contrib/README.md +12 -0
  9. check_opencloud_security-1.0.1/opencloud_local_scan/README.md +399 -0
  10. check_opencloud_security-1.0.1/opencloud_local_scan/__init__.py +119 -0
  11. check_opencloud_security-1.0.1/opencloud_local_scan/cli.py +184 -0
  12. check_opencloud_security-1.0.1/opencloud_local_scan/config.py +278 -0
  13. check_opencloud_security-1.0.1/opencloud_local_scan/data/release_schedule.json +235 -0
  14. check_opencloud_security-1.0.1/opencloud_local_scan/data/vulnerabilities.json +19 -0
  15. check_opencloud_security-1.0.1/opencloud_local_scan/factory.py +139 -0
  16. check_opencloud_security-1.0.1/opencloud_local_scan/hardening.py +324 -0
  17. check_opencloud_security-1.0.1/opencloud_local_scan/releases.py +326 -0
  18. check_opencloud_security-1.0.1/opencloud_local_scan/scanner.py +1331 -0
  19. check_opencloud_security-1.0.1/opencloud_local_scan/secrets.py +129 -0
  20. check_opencloud_security-1.0.1/opencloud_local_scan/service.py +243 -0
  21. check_opencloud_security-1.0.1/opencloud_local_scan/versions.py +581 -0
  22. check_opencloud_security-1.0.1/opencloud_local_scan/vulndb.py +344 -0
  23. check_opencloud_security-1.0.1/pyproject.toml +52 -0
  24. check_opencloud_security-1.0.1/secrets/README.md +37 -0
  25. check_opencloud_security-1.0.1/tests/__init__.py +0 -0
  26. check_opencloud_security-1.0.1/tests/conftest.py +27 -0
  27. check_opencloud_security-1.0.1/tests/fake_opencloud.py +276 -0
  28. check_opencloud_security-1.0.1/tests/test_config.py +294 -0
  29. check_opencloud_security-1.0.1/tests/test_e2e_cli.py +261 -0
  30. check_opencloud_security-1.0.1/tests/test_env_config.py +251 -0
  31. check_opencloud_security-1.0.1/tests/test_explain.py +426 -0
  32. check_opencloud_security-1.0.1/tests/test_local_scanner.py +365 -0
  33. check_opencloud_security-1.0.1/tests/test_multi_host.py +243 -0
  34. check_opencloud_security-1.0.1/tests/test_perfdata.py +393 -0
  35. check_opencloud_security-1.0.1/tests/test_rating_thresholds.py +156 -0
  36. check_opencloud_security-1.0.1/tests/test_release_schedule.py +393 -0
  37. check_opencloud_security-1.0.1/tests/test_releases.py +352 -0
  38. check_opencloud_security-1.0.1/tests/test_service.py +237 -0
  39. check_opencloud_security-1.0.1/tests/test_update_script.py +314 -0
  40. check_opencloud_security-1.0.1/tests/test_vulndb.py +249 -0
  41. check_opencloud_security-1.0.1/tests/test_waivers.py +373 -0
  42. check_opencloud_security-1.0.1/tests/test_webhook.py +198 -0
@@ -0,0 +1,247 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ # Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ # poetry.lock
109
+ # poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ # pdm.lock
116
+ # pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ # pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ # .idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+ # Temporary file for partial code execution
204
+ tempCodeRunnerFile.py
205
+
206
+ # Ruff stuff:
207
+ .ruff_cache/
208
+
209
+ # PyPI configuration file
210
+ .pypirc
211
+
212
+ # Marimo
213
+ marimo/_static/
214
+ marimo/_lsp/
215
+ __marimo__/
216
+
217
+ # Streamlit
218
+ .streamlit/secrets.toml
219
+
220
+ # --- check-opencloud-security -----------------------------------------------
221
+ # uv.lock is committed on purpose: it is what makes an install reproducible,
222
+ # and CI installs from it (see CONTRIBUTING.md).
223
+ !/uv.lock
224
+
225
+ /.nox
226
+ /.mypy_cache
227
+
228
+ # Local configuration; only the committed example is tracked.
229
+ /check-opencloud-security.yml
230
+ /check-opencloud-security.yaml
231
+ /config/*.yml
232
+ /config/*.yaml
233
+ !/config/*.example.yml
234
+
235
+ # Local secret files for docker-compose (only the *.example files are tracked)
236
+ /secrets/*
237
+ !/secrets/*.example
238
+ !/secrets/README.md
239
+
240
+ # Generated with `uv export` when a deployment tool needs it - never committed
241
+ /requirements.txt
242
+ /requirements-*.txt
243
+
244
+ # Editor state
245
+ .idea/
246
+ .vscode/
247
+ /.ansible/
@@ -0,0 +1,156 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ New entries are added on top by the release workflow whenever the version in
9
+ `pyproject.toml` changes; the same entry is written to `RELEASE.md` and used as
10
+ the body of the GitHub release.
11
+
12
+ ## [1.0.1] - 2026-08-12
13
+
14
+ ### Changed
15
+
16
+ - Bumped version to 1.0.1
17
+ - Version 1.0.0 commit
18
+ - Initial commit
19
+
20
+ ### Documentation
21
+
22
+ - Release notes for v1.0.0 [skip ci]
23
+
24
+ ## [1.0.0] - 2026-08-12
25
+
26
+ First release. A Nagios/Icinga plugin that checks an OpenCloud instance for
27
+ known vulnerabilities and misconfiguration. OpenCloud has no public scan API,
28
+ so the plugin scans entirely on its own. The ratings follow the scale of the
29
+ Nextcloud scan API, so that existing thresholds and dashboards keep their
30
+ meaning.
31
+
32
+ ### Added
33
+
34
+ - **Built-in scanner** (`opencloud_local_scan`). Reads `/status.php` and the
35
+ unauthenticated capabilities endpoint, evaluates security headers, and rates
36
+ the instance on a `0`-`5` scale. No data about the instance leaves the
37
+ network; hostnames, IP addresses, IPv6 and custom ports are all accepted.
38
+ - **OpenCloud-specific checks**: unauthenticated WebDAV, Graph and OCS
39
+ endpoints; exposed `opencloud.yaml`, `proxy/server.key`, the idm boltdb,
40
+ `.env` and `.git/config`; reachable service debug ports and `/metrics`,
41
+ `/config`, `/debug/pprof` handlers; enabled HTTP basic authentication;
42
+ version disclosure via response headers and WebFinger; directory listings;
43
+ maintenance mode and pending database upgrades.
44
+ - **Catch-all detection.** OpenCloud's single-page frontend answers unknown
45
+ paths with HTTP 200, so the scanner learns what a nonexistent path looks
46
+ like before reporting any path as exposed.
47
+ - **TLS inspection** with graceful degradation: verified HTTPS, then
48
+ unverified HTTPS with a `tlsTrusted` finding, then plain HTTP with a
49
+ critical `httpsAvailable` finding. Covers handshake, protocol version and
50
+ certificate expiry.
51
+ - **Correct version handling.** `/status.php` reports hardcoded legacy
52
+ `version`/`versionstring` fields for old sync clients; only `productversion`
53
+ is the real release. Instances that offer nothing else are reported as
54
+ `legacyVersion` instead of being rated against a version that means nothing.
55
+ - **Hardening reporting** derived from what the instance actually reports -
56
+ HSTS strength, CSP quality, basic auth, public-link password and expiry
57
+ enforcement, user enumeration and password policy - rather than inferred
58
+ from the version number.
59
+ - **Update check** against the OpenCloud release feed on GitHub, with `auto`,
60
+ `feed`, `pinned`, `bundled` and `off` modes. The offline modes and the
61
+ automatic fallback in `auto` keep an air-gapped or rate-limited setup
62
+ working.
63
+ - **Lifecycle-aware end-of-life detection.** OpenCloud maintains three kinds
64
+ of releases side by side, and each has its own support window: *rolling*
65
+ (a release roughly every three weeks, only the newest one receives fixes),
66
+ *production* (roughly every six months, kept alive with patch releases until
67
+ the next production release takes over) and *LTS* (a production line with
68
+ two years of backports). The verdict follows the published release schedule
69
+ in `opencloud_local_scan/data/release_schedule.json`, refreshed on release
70
+ and monthly by a scheduled workflow, because a flat list of major releases
71
+ cannot express three overlapping support windows.
72
+ - **`releaseType` and `lifecycle` in the result document**, reporting the
73
+ release line, its track, its release date, when support ends, how many days
74
+ are left and which release to upgrade to. The plugin prints a
75
+ `Release lifecycle:` line and a `support_days_left` performance value, and
76
+ the webhook payload carries both fields.
77
+ - **The update check is track aware.** A release feed only knows the newest
78
+ release overall, and on OpenCloud that is always a rolling one. Offering it
79
+ to a production or LTS instance would silently move it onto a track with a
80
+ three-week support window, so those instances are offered the newest release
81
+ of their own track instead. `UpdateInfo` carries `track` and
82
+ `newestRelease`, so the newest release overall is reported but not presented
83
+ as the thing to install.
84
+ - **`--release-track` declares which track an instance follows** (`rolling`,
85
+ `production` or `lts`). Without it a version is judged as generously as is
86
+ true, which is right when nobody has said otherwise but wrong for anyone
87
+ deliberately on the rolling track, where `7.2.3` went out of support the day
88
+ `7.4.0` shipped. With a declared track the version is judged on that track
89
+ alone, the update recommendation follows it, and the output marks the track
90
+ as declared rather than inferred. A version that was never published on its
91
+ declared track is reported with the reason rather than an empty support
92
+ date, and is never told to "upgrade" to an older release.
93
+ - **`--ignore-hardening` accepts a finding you are not going to fix.** Some
94
+ findings are real but not actionable in a given environment - a CSP that
95
+ cannot be tightened without breaking the web UI, an HSTS header owned by a
96
+ reverse proxy. The rating is recalculated without the waived finding, so
97
+ accepting one genuinely changes the grade instead of leaving the check
98
+ permanently yellow. The option is repeatable, takes a comma-separated list,
99
+ understands shell-style wildcards (`debugPort:*`), and matches hardening
100
+ measures, security headers, `httpsEnforced` and additional-check ids alike -
101
+ one option for all of them, because `basicAuthDisabled` is both a hardening
102
+ measure and an additional check. A waiver hides an alert, not the evidence:
103
+ waived findings drop out of the alert lines, the `hardenings_missing` and
104
+ `extra_checks_failed` metrics and the webhook payload, but stay in the
105
+ result document flagged with `"ignored": true` and are listed as
106
+ `Ignored by configuration (n): ...` in the output. Only a finding that
107
+ actually failed can be waived, and no waiver can clear an end-of-life
108
+ release.
109
+ - **`--debug` explains the rating.** A grade on its own is a verdict without
110
+ an argument, so the check can show its reasoning: where the rating started
111
+ (version and advisory database), which failed check capped it and by how
112
+ much, the final value, and the thresholds that turned it into a WARNING or
113
+ CRITICAL. A failed check that did *not* decide the outcome is listed too,
114
+ marked as such, so nothing looks quietly dropped. The same breakdown is
115
+ available as structured data in `ratingExplanation`, sorted by severity so
116
+ it does not depend on the order the checks happened to run in.
117
+ - **Every hardening identifier is explained.** `basicAuthDisabled` and
118
+ `cspWithoutUnsafeInline` say nothing to someone who has to fix them, so the
119
+ `opencloud_local_scan.hardening` catalogue pairs each flag with a
120
+ plain-language meaning, the OpenCloud environment variable that governs it
121
+ (`PROXY_ENABLE_BASIC_AUTH`,
122
+ `OC_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD`,
123
+ `PROXY_CSP_CONFIG_FILE_LOCATION`, ...) and a link to the documentation.
124
+ - **Findings that no setting can clear are recorded but never alerted on.**
125
+ Public-link expiry is hardcoded in OpenCloud, so `publicLinkExpirationEnforced`
126
+ fails on every instance and no operator can fix it; alerting on it trains
127
+ people to ignore the hardening line altogether. Such flags stay in the
128
+ result document and in `--debug`, but are kept out of the alert line, the
129
+ `hardenings_missing` metric and the webhook.
130
+ - **Advisory database** matching on the half-open range
131
+ `[introduced, fixed)`, accepting the native, GitHub Advisory and OSV
132
+ formats from the bundled file, extra files and a remote feed.
133
+ - **`check-opencloud-scanner`** with `scan` (one-shot JSON) and `serve`
134
+ (HTTP service with `/api/queue`, `/api/result/<uuid>`, `/api/requeue`,
135
+ `/api/scan` and `/healthz`, optional token auth and a per-host result
136
+ cache).
137
+ - **Configuration** from a YAML file, `COS_`-prefixed environment variables or
138
+ a secret provider (`secret://`, `file://`, `env://`, opt-in `exec://`),
139
+ with command line > environment > file > default precedence. This includes
140
+ `scanner.release_schedule` for sites with vendor support commitments that
141
+ differ from the public ones, `scanner.release_track` and
142
+ `scanner.ignore_hardenings`.
143
+ - **Monitoring integration**: Nagios/Icinga exit codes, performance data
144
+ (`rating`, `vulnerabilities`, `time`, `hardenings_missing`,
145
+ `extra_checks_failed`, `update_available`, `support_days_left`),
146
+ configurable WARNING/CRITICAL thresholds, optional hardening evaluation,
147
+ multi-host runs reporting the worst state, retries with exponential backoff,
148
+ and optional webhook notifications that never change the reported state.
149
+ - **Deployment**: a Docker image and `docker-compose.yml`, an Ansible role,
150
+ systemd service/timer and cron examples, Icinga2 and Icinga Director command
151
+ definitions.
152
+ - Documentation, a test suite covering the scanner, service, configuration,
153
+ release lookup, thresholds, webhooks, multi-host handling and the plugin
154
+ end to end as a real subprocess, and CI workflows for tests, ruff, mypy,
155
+ bandit, dependency review, multi-version nox runs, PyPI publishing and the
156
+ monthly release-schedule refresh.