Files
computer-playbook/filter_plugins/sed.py
T
kevinveenbirkenbach 3b1032895a feat(security): introduce sed_escape filter and enforce safe sed replacements
- add sed_escape Jinja filter for sed replacement safety
  - escapes \, &, and the active sed delimiter
  - designed explicitly for sed s/// replacement context

- patch all sed-based config updates across roles
  - espocrm, friendica, gitea, matomo, moodle, wordpress
  - ensure every Jinja value in sed replacements uses sed_escape
  - use correct delimiter-specific escaping (/, |, #)

- add robust integration test to enforce sed_escape usage
  - scans all roles/*/tasks/**/*.yml
  - detects sed substitutions with Jinja in replacement part
  - ignores delimiters inside {{ ... }} blocks
  - fails CI if sed_escape is missing

- harden repository against sed injection, delimiter breakage,
  and credential corruption in config patching tasks

https://chatgpt.com/share/6974fb45-1ee8-800f-80bd-c71122e4ac02
2026-01-24 18:02:37 +01:00

52 lines
1.1 KiB
Python

# filter_plugins/sed.py
#
# sed-safe escaping for replacement strings.
#
# Intended usage:
# sed "s<d>PATTERN<d>{{ value | sed_escape('<d>') }}<d>"
#
# Escapes:
# - backslash (\)
# - ampersand (&) → sed replacement reference
# - delimiter (default: |)
#
# Does NOT:
# - add quotes
# - perform shell escaping
#
# Safe for:
# - sed replacement part
# - passwords containing $, ', ", spaces, /, etc.
#
from __future__ import annotations
class FilterModule:
def filters(self):
return {
"sed_escape": self.sed_escape,
}
@staticmethod
def sed_escape(value, delimiter: str = "|") -> str:
"""
Escape a value for use as the *replacement* part of a sed s/// command.
Example:
{{ password | sed_escape('|') }}
"""
if value is None:
return ""
s = str(value)
# Escape order matters
s = s.replace("\\", "\\\\") # backslash
s = s.replace("&", "\\&") # sed match reference
if delimiter:
s = s.replace(delimiter, "\\" + delimiter)
return s