Compare commits

..

1 Commits

Author SHA1 Message Date
Jason Novinger
57fe5ee1ea Fixes #20442: Mark template-accessible methods with alters_data=True
Add alters_data=True to methods that modify database or filesystem state
and are accessible from Jinja2 sandbox template contexts:

- UserConfig.set(), clear(): Persist preference changes when commit=True
- ManagedFile.sync_data(): Writes files to scripts/reports storage
- ScriptModule.sync_classes(), sync_data(): Creates/deletes Script objects
- Job.start(), terminate(): Updates job status, creates notifications

Methods intentionally not protected:
- DataFile.refresh_from_disk(): Only modifies instance attributes in memory
- Overridden save()/delete(): Django's AltersData mixin auto-propagates
- Properties like Script.python_class: Not callable in template context

Ref: #20356 for exploit details demonstrating the vulnerability
2026-02-13 06:53:28 -06:00
9 changed files with 9 additions and 63 deletions

View File

@@ -200,48 +200,6 @@ REDIS = {
!!! note
It is permissible to use Sentinel for only one database and not the other.
### SSL Configuration
If you need to configure SSL/TLS for Redis beyond the basic `SSL`, `CA_CERT_PATH`, and `INSECURE_SKIP_TLS_VERIFY` options (for example, client certificates, a specific TLS version, or custom ciphers), you can pass additional parameters via the `KWARGS` key in either the `tasks` or `caching` subsection.
NetBox already maps `CA_CERT_PATH` to `ssl_ca_certs` and (for caching) `INSECURE_SKIP_TLS_VERIFY` to `ssl_cert_reqs`; only add `KWARGS` when you need to override or extend those settings (for example, to supply client certificates or restrict TLS version or ciphers).
* `KWARGS` - Optional dictionary of additional SSL/TLS (or other) parameters passed to the Redis client. These are passed directly to the underlying Redis client: for `tasks` to [redis-py](https://redis-py.readthedocs.io/en/stable/connections.html), and for `caching` to the [django-redis](https://github.com/jazzband/django-redis#configure-as-cache-backend) connection pool.
Example:
```python
REDIS = {
'tasks': {
'HOST': 'redis.example.com',
'PORT': 1234,
'SSL': True,
'CA_CERT_PATH': '/etc/ssl/certs/ca.crt',
'KWARGS': {
'ssl_certfile': '/path/to/client-cert.pem',
'ssl_keyfile': '/path/to/client-key.pem',
'ssl_min_version': ssl.TLSVersion.TLSv1_2,
'ssl_ciphers': 'HIGH:!aNULL',
},
},
'caching': {
'HOST': 'redis.example.com',
'PORT': 1234,
'SSL': True,
'CA_CERT_PATH': '/etc/ssl/certs/ca.crt',
'KWARGS': {
'ssl_certfile': '/path/to/client-cert.pem',
'ssl_keyfile': '/path/to/client-key.pem',
'ssl_min_version': ssl.TLSVersion.TLSv1_2,
'ssl_ciphers': 'HIGH:!aNULL',
},
}
}
```
!!! note
If you use `ssl.TLSVersion` in your configuration (e.g. `ssl_min_version`), add `import ssl` at the top of your configuration file.
---
## SECRET_KEY

View File

@@ -89,6 +89,7 @@ class ManagedFile(SyncedDataMixin, models.Model):
with storage.open(self.full_path, 'wb+') as new_file:
new_file.write(self.data_file.data)
sync_data.alters_data = True
@cached_property
def storage(self):

View File

@@ -216,6 +216,7 @@ class Job(models.Model):
# Send signal
job_start.send(self)
start.alters_data = True
def terminate(self, status=JobStatusChoices.STATUS_COMPLETED, error=None):
"""
@@ -245,6 +246,7 @@ class Job(models.Model):
# Send signal
job_end.send(self)
terminate.alters_data = True
def log(self, record: logging.LogRecord):
"""

View File

@@ -307,6 +307,7 @@ class ScriptViewSet(ModelViewSet):
"""
Run a Script identified by its numeric PK or module & name and return the pending Job as the result
"""
script = self._get_script(pk)
if not request.user.has_perm('extras.run_script', obj=script):

View File

@@ -178,9 +178,11 @@ class ScriptModule(PythonModuleMixin, JobsMixin, ManagedFile):
name=name,
is_executable=True,
)
sync_classes.alters_data = True
def sync_data(self):
super().sync_data()
sync_data.alters_data = True
def save(self, *args, **kwargs):
self.file_root = ManagedFileRootPathChoices.SCRIPTS

View File

@@ -408,11 +408,6 @@ if CACHING_REDIS_CA_CERT_PATH:
CACHES['default']['OPTIONS'].setdefault('CONNECTION_POOL_KWARGS', {})
CACHES['default']['OPTIONS']['CONNECTION_POOL_KWARGS']['ssl_ca_certs'] = CACHING_REDIS_CA_CERT_PATH
# Merge in KWARGS for additional parameters
if caching_redis_kwargs := REDIS['caching'].get('KWARGS'):
CACHES['default']['OPTIONS'].setdefault('CONNECTION_POOL_KWARGS', {})
CACHES['default']['OPTIONS']['CONNECTION_POOL_KWARGS'].update(caching_redis_kwargs)
#
# Sessions
@@ -822,11 +817,6 @@ if TASKS_REDIS_CA_CERT_PATH:
RQ_PARAMS.setdefault('REDIS_CLIENT_KWARGS', {})
RQ_PARAMS['REDIS_CLIENT_KWARGS']['ssl_ca_certs'] = TASKS_REDIS_CA_CERT_PATH
# Merge in KWARGS for additional parameters
if tasks_redis_kwargs := TASKS_REDIS.get('KWARGS'):
RQ_PARAMS.setdefault('REDIS_CLIENT_KWARGS', {})
RQ_PARAMS['REDIS_CLIENT_KWARGS'].update(tasks_redis_kwargs)
# Define named RQ queues
RQ_QUEUES = {
RQ_QUEUE_HIGH: RQ_PARAMS,

File diff suppressed because one or more lines are too long

View File

@@ -5,16 +5,6 @@
font-variant-ligatures: none;
}
// TODO: Remove when Tabler releases fix for https://github.com/tabler/tabler/issues/2271
// and NetBox upgrades to that version. Fix merged to Tabler dev branch in PR #2548.
:root,
:host {
@include media-breakpoint-up(lg) {
margin-left: 0;
scrollbar-gutter: stable;
}
}
// Restore default foreground & background colors for <pre> blocks
pre {
background-color: transparent;

View File

@@ -113,6 +113,7 @@ class UserConfig(models.Model):
if commit:
self.save()
set.alters_data = True
def clear(self, path, commit=False):
"""
@@ -140,3 +141,4 @@ class UserConfig(models.Model):
if commit:
self.save()
clear.alters_data = True