a7dc917021
ISSUES CLOSED: #319
109 lines
2.8 KiB
Python
109 lines
2.8 KiB
Python
"""Robot Framework helper for secure template rendering.
|
|
|
|
Provides a CLI-style interface for Robot to invoke the secure
|
|
template renderer. Exit code 0 = success, 1 = failure.
|
|
|
|
Usage::
|
|
|
|
python robot/helper_security_templates.py render \\
|
|
<template> <key1=val1> <key2=val2> ...
|
|
python robot/helper_security_templates.py validate <template>
|
|
python robot/helper_security_templates.py reject <template>
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Ensure the src directory is on the import path.
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.insert(0, _SRC)
|
|
|
|
from cleveragents.templates.secure_renderer import ( # noqa: E402
|
|
SecureTemplateRenderer,
|
|
TemplateConfig,
|
|
TemplateError,
|
|
validate_template,
|
|
)
|
|
|
|
|
|
def _cmd_render(args: list[str]) -> int:
|
|
"""Render a template with key=value context pairs."""
|
|
if len(args) < 1:
|
|
print("Usage: render <template> [key=value ...]")
|
|
return 1
|
|
template = args[0]
|
|
context: dict[str, str] = {}
|
|
for pair in args[1:]:
|
|
if "=" in pair:
|
|
k, v = pair.split("=", 1)
|
|
context[k] = v
|
|
renderer = SecureTemplateRenderer(config=TemplateConfig())
|
|
try:
|
|
result = renderer.render(template, context)
|
|
print(f"sec-render-ok: {result}")
|
|
return 0
|
|
except TemplateError as exc:
|
|
print(f"sec-render-err: {exc}")
|
|
return 1
|
|
|
|
|
|
def _cmd_validate(args: list[str]) -> int:
|
|
"""Validate a template for security issues."""
|
|
if len(args) < 1:
|
|
print("Usage: validate <template>")
|
|
return 1
|
|
template = args[0]
|
|
errors = validate_template(template)
|
|
if errors:
|
|
for err in errors:
|
|
print(f"sec-validate-err: {err}")
|
|
return 1
|
|
print("sec-validate-ok: clean")
|
|
return 0
|
|
|
|
|
|
def _cmd_reject(args: list[str]) -> int:
|
|
"""Verify that a template is rejected as unsafe."""
|
|
if len(args) < 1:
|
|
print("Usage: reject <template>")
|
|
return 1
|
|
template = args[0]
|
|
renderer = SecureTemplateRenderer(config=TemplateConfig())
|
|
try:
|
|
renderer.render(template, {})
|
|
print("sec-reject-fail: template was accepted")
|
|
return 1
|
|
except TemplateError as exc:
|
|
print(f"sec-reject-ok: {exc}")
|
|
return 0
|
|
|
|
|
|
def main() -> int:
|
|
"""Entry point called by Robot Framework ``Run Process``."""
|
|
if len(sys.argv) < 2:
|
|
print("Usage: helper_security_templates.py <command> ...")
|
|
return 1
|
|
|
|
command = sys.argv[1]
|
|
args = sys.argv[2:]
|
|
|
|
dispatch = {
|
|
"render": _cmd_render,
|
|
"validate": _cmd_validate,
|
|
"reject": _cmd_reject,
|
|
}
|
|
|
|
handler = dispatch.get(command)
|
|
if handler is None:
|
|
print(f"Unknown command: {command}")
|
|
return 1
|
|
|
|
return handler(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|