shared/templates/firefox_policy-setting/template.py
from __future__ import print_function
import re
import json
def normalize_policy_path(path_string):
"""Generate a normalized path array from yaml.
Paths are space-separated key names.
All firefox policies are under the policies key.
"""
path = []
if len(path_string.strip()) > 0:
path = re.split(" ", path_string)
if path[0] != 'policies':
path = ['policies'] + path
return path
def make_json_boolean_match_regex(json_value):
if json_value.lower() in ["false", "true"]:
return r"[{0}{1}]{2}".format(json_value[0].upper(),
json_value[0].lower(),
json_value[1:]
)
return "\"{0}\"".format(json_value)
def make_json_boolean_remediate(json_value):
if json_value.lower() in ["false", "true"]:
return json_value.lower() == "true"
return "\'{0}\'".format(json_value)
# These regexes work under the complexities currently observed with the Firefox
# policies.json, which is (so far) a subset of JSON that is just nested keys.
# No JSON arrays are supported.
# Remove the moment that OpenSCAP/OVAL spec supports a JSON configuration probe.
OVAL_MATCH_ANY_FOLLOWING = r"[^}]*\}"
OVAL_MATCH_OPEN_BRACES_FOR_BLOCK = r"\{[\s\S]*?"
OVAL_MATCH_ANYTHING_AFTER_OPEN_BRACE = r"\{[\s\S]*"
# Note: str.format() notation.
OVAL_MATCH_JSON_KEY = r'(?="{0}")"{0}"[\s]*:[\s]*'
def regex_escape(_search_string):
return re.escape(_search_string)
def build_oval_search_regex_for_json_parameter(_path, parameter):
parameter = r'(?=[^"]){0}"[\s]*:[\s]*([^,}}]+),?'.format(parameter)
_result = parameter
for p in reversed(_path):
depth_match_block = OVAL_MATCH_OPEN_BRACES_FOR_BLOCK
if p == "policies":
depth_match_block = OVAL_MATCH_ANYTHING_AFTER_OPEN_BRACE
_result = ''.join([OVAL_MATCH_JSON_KEY.format(regex_escape(p)),
depth_match_block,
_result])
_result = r'^(?i)\{\s*' + _result + r'\s*'
return _result
def build_python_json_notation(_path, depth=0):
"""Generate the Python call syntax for this path within a given distance
from the top-level node.
Parameters:
_path: the list of the entire path in the JSON document to this
particular key.
depth: How far along the path to generate this syntax.
"""
if depth > len(_path):
return r''
_result = r''
for p in list(reversed(_path))[depth:]:
_result = ''.join(["['{0}']".format(p), _result])
return _result
def build_test_json(policy, _test, missing=False, wrong=False):
"""Generate a minimal JSON configuration for this particular policy object, based
on a correct (set to value), missing, or wrong value.
Returns a dict() containing this test entry.
Parameters:
policy: current policy item to generate for
_test: dict() that has at least the value {"policies": {} }
"""
_current = _test.get("policies")
_key = policy["parameter"]
_value = make_json_boolean_remediate(policy["value"])
for p in policy["subpath"]:
if p not in _current:
_current[p] = dict()
_current = _current.get(p)
if wrong:
_current[_key] = "VERYVERYBADVALUE"
elif not missing:
_current[_key] = _value
return _test
def preprocess(data, lang):
"""Generate the appropriate regex for each policy path, search values, etc.
Expects a dict of template arguments:
path - Space-separated path in JSON to dictate where the parameter is. "policies" is
added to the front if not present or used if path is not present.
parameter - Parameter name to search for the state of the value.
value - Expected value to use for remediation.
search_value (optional) - custom regular expression to use in the match for value.
"""
_test_correct_config = {"policies": {}}
_test_bad_missing = {"policies": {}}
_test_bad_wrong = {"policies": {}}
for i, _policy in enumerate(data.get("policies", [])):
_path = normalize_policy_path(_policy.get("path", ""))
# regex used in OVAL for detection
_policy["oval_regex"] = build_oval_search_regex_for_json_parameter(_path,
_policy["parameter"])
_policy["oval_state"] = make_json_boolean_match_regex(_policy["value"])
# Implement override for search_value
if _policy.get("search_value", None):
_policy["oval_state"] = _policy["search_value"]
# path without "policies"
_policy["subpath"] = _path[1:]
# Precached notation for addressing JSON path
_policy["path_python"] = [build_python_json_notation(_path, x)
for x in range(0, len(_path) + 1)][:-1]
# JSON path as iterable list, sans "policies"
_policy["subpath_string"] = '_'.join(_path[1:])
# entire JSON path to be set as an iterable list.
_policy["path_list"] = _path
# value with quotes added for simple insertion.
_policy["value_escaped"] = make_json_boolean_remediate(_policy["value"])
# correct_value.pass tests
build_test_json(_policy,
_test_correct_config,
missing=False,
wrong=False)
# missing_value.fail tests
build_test_json(_policy,
_test_bad_missing,
missing=True,
wrong=False)
# wrong_value.fail tests
build_test_json(_policy,
_test_bad_wrong,
missing=False,
wrong=True)
# Reassign the modified policy entry.
data["policies"][i] = _policy
# Pretty-print the output for our test JSON to variables
# so that it can be used in the test templates.
data["_correct_config"] = json.dumps(_test_correct_config, indent=4, separators=(',', ': '))
data["_bad_missing"] = json.dumps(_test_bad_missing, indent=4, separators=(',', ': '))
data["_bad_wrong"] = json.dumps(_test_bad_wrong, indent=4, separators=(',', ': '))
return data