Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions demo-api/restrictedpython-security.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
name: restrictedpython_security_validation
path: snippets/
headers:
Authorization: Token ${token}
requests:
# Test 1: Allowed modules should work
- name: test_allowed_modules
method: post
body:
title: "Allowed Modules Test"
code: "# Testing allowed modules"
language: "python"
# Test all allowed modules from ALLOWED_MODULES
datetime_now: ${{ datetime.datetime.now().isoformat() }}
math_sqrt: ${{ math.sqrt(16) }}
random_int: ${{ random.randint(1, 10) }}
regex_match: ${{ bool(re.match("a", "abc")) }}
time_now: ${{ time.time() }}
uuid_generated: ${{ str(uuid.uuid4()) }}
tests:
- name: status_code_is_201
assert: ${{ response.status_code == 201 }}
- name: allowed_modules_work
assert: ${{ response.json()["math_sqrt"] == 4.0 }}
- name: datetime_module_works
assert: ${{ len(response.json()["datetime_now"]) > 10 }}
- name: random_in_range
assert: ${{ 1 <= response.json()["random_int"] <= 10 }}
- name: regex_works
assert: ${{ response.json()["regex_match"] == True }}
- name: time_is_numeric
assert: ${{ isinstance(response.json()["time_now"], (int, float)) }}
- name: uuid_is_string
assert: ${{ isinstance(response.json()["uuid_generated"], str) }}

# Test 2: Unicode handling
- name: test_unicode_handling
method: post
body:
title: "Unicode Test"
code: "# Testing unicode"
language: "python"
# Unicode string operations
reversed_unicode: ${{ "áéíóú"[::-1] }}
unicode_length: ${{ len("áéíóú") }}
unicode_upper: ${{ "hello ñoño".upper() }}
unicode_title: ${{ "josé maría".title() }}
tests:
- name: status_code_is_201
assert: ${{ response.status_code == 201 }}
- name: unicode_reverse_works
assert: ${{ response.json()["reversed_unicode"] == "úóíéá" }}
- name: unicode_length_correct
assert: ${{ response.json()["unicode_length"] == 5 }}
- name: unicode_upper_works
assert: ${{ "ÑOÑO" in response.json()["unicode_upper"] }}

# Test 3: Isolation between requests
- name: test_isolation_first
method: post
body:
title: "Isolation Test 1"
code: "# First isolation test"
language: "python"
# These should be isolated from each other
string_length_a: ${{ len("a") }}
test_value: ${{ 42 }}
vars:
first_result: ${{ response.json()["string_length_a"] }}
tests:
- name: status_code_is_201
assert: ${{ response.status_code == 201 }}
- name: first_length_correct
assert: ${{ response.json()["string_length_a"] == 1 }}

- name: test_isolation_second
method: post
body:
title: "Isolation Test 2"
code: "# Second isolation test"
language: "python"
# Should not have access to previous execution state
string_length_bb: ${{ len("bb") }}
different_value: ${{ 84 }}
tests:
- name: status_code_is_201
assert: ${{ response.status_code == 201 }}
- name: second_length_correct
assert: ${{ response.json()["string_length_bb"] == 2 }}
- name: isolation_verified
assert: ${{ response.json()["string_length_bb"] != first_result }}

# Test 4: Complex safe expressions
- name: test_complex_safe_expressions
method: post
body:
title: "Complex Safe Expressions"
code: "# Complex expressions test"
language: "python"
# Test complex but safe operations
list_comprehension: ${{ [x * 2 for x in range(1, 6)] }}
nested_dict: ${{ {"data": {"nested": {"value": 123}}} }}
string_operations: ${{ "hello world".replace("world", "RestrictedPython").title() }}
math_combinations: ${{ math.ceil(math.sqrt(math.pow(4, 2))) }}
datetime_arithmetic: ${{ (datetime.datetime.now() + datetime.timedelta(days=1)).strftime('%Y-%m-%d') }}
tests:
- name: status_code_is_201
assert: ${{ response.status_code == 201 }}
- name: list_comprehension_works
assert: ${{ response.json()["list_comprehension"] == [2, 4, 6, 8, 10] }}
- name: nested_dict_access_works
assert: ${{ response.json()["nested_dict"]["data"]["nested"]["value"] == 123 }}
- name: string_ops_work
assert: ${{ response.json()["string_operations"] == "Hello Restrictedpython" }}
- name: math_combinations_work
assert: ${{ response.json()["math_combinations"] == 4 }}

# Test 5: Edge cases and boundary conditions
- name: test_edge_cases
method: post
body:
title: "Edge Cases Test"
code: "# Edge cases"
language: "python"
# Test edge cases that should work safely
empty_string_len: ${{ len("") }}
zero_math: ${{ math.sqrt(0) }}
negative_abs: ${{ abs(-42) }}
boolean_operations: ${{ True and False or True }}
none_check: ${{ None is None }}
type_checking: ${{ isinstance([], list) }}
tests:
- name: status_code_is_201
assert: ${{ response.status_code == 201 }}
- name: empty_string_len_zero
assert: ${{ response.json()["empty_string_len"] == 0 }}
- name: zero_sqrt_works
assert: ${{ response.json()["zero_math"] == 0.0 }}
- name: abs_works
assert: ${{ response.json()["negative_abs"] == 42 }}
- name: boolean_logic_works
assert: ${{ response.json()["boolean_operations"] == True }}
- name: none_check_works
assert: ${{ response.json()["none_check"] == True }}
- name: type_check_works
assert: ${{ response.json()["type_checking"] == True }}
112 changes: 112 additions & 0 deletions demo-api/restrictedpython-unsafe-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: restrictedpython_unsafe_operations
path: snippets/
headers:
Authorization: Token ${token}
requests:
# Test: Verify that unsafe operations documentation exists
# These tests document what SHOULD be blocked by RestrictedPython
# The expressions themselves are commented to avoid execution

- name: test_disallowed_modules_documentation
method: post
body:
title: "Disallowed Modules Documentation"
code: "# These modules should be blocked by RestrictedPython"
language: "python"
# Document what should be blocked (as comments for safety):
# BLOCKED: import os; os.system("ls")
# BLOCKED: import sys; sys.exit(0)
# BLOCKED: import subprocess; subprocess.run(["ls"])
# BLOCKED: __import__('os').system('ls')
security_test_category: "disallowed_modules"
expected_behavior: "RestrictedPython should prevent importing os, sys, subprocess"
tests:
- name: status_code_is_201
assert: ${{ response.status_code == 201 }}
- name: documentation_created
assert: ${{ response.json()["security_test_category"] == "disallowed_modules" }}

- name: test_dangerous_builtins_documentation
method: post
body:
title: "Dangerous Built-ins Documentation"
code: "# These built-ins should be blocked"
language: "python"
# Document what should be blocked (as comments for safety):
# BLOCKED: open("file.txt", "r")
# BLOCKED: exec("print('dangerous')")
# BLOCKED: eval("2+2")
# BLOCKED: compile("print(1)", "<string>", "exec")
security_test_category: "dangerous_builtins"
expected_behavior: "RestrictedPython should block open, exec, eval, compile"
tests:
- name: status_code_is_201
assert: ${{ response.status_code == 201 }}
- name: documentation_created
assert: ${{ response.json()["security_test_category"] == "dangerous_builtins" }}

- name: test_restricted_attributes_documentation
method: post
body:
title: "Restricted Attributes Documentation"
code: "# These attribute accesses should be blocked"
language: "python"
# Document what should be blocked (as comments for safety):
# BLOCKED: ().__class__.__mro__
# BLOCKED: [].__class__.__base__.__subclasses__()
# BLOCKED: "".__class__.__mro__[1].__subclasses__()
# BLOCKED: (lambda:0).__globals__
# BLOCKED: type.__subclasses__(type)
security_test_category: "restricted_attributes"
expected_behavior: "RestrictedPython should block dangerous attribute access"
tests:
- name: status_code_is_201
assert: ${{ response.status_code == 201 }}
- name: documentation_created
assert: ${{ response.json()["security_test_category"] == "restricted_attributes" }}

- name: test_side_effects_prevention_documentation
method: post
body:
title: "Side Effects Prevention Documentation"
code: "# These side effects should be prevented"
language: "python"
# Document what should be blocked (as comments for safety):
# BLOCKED: globals()["x"] = 1
# BLOCKED: setattr(object(), "attr", "value")
# BLOCKED: delattr(object(), "attr")
# BLOCKED: vars()["new_var"] = "dangerous"
security_test_category: "side_effects_prevention"
expected_behavior: "RestrictedPython should prevent global state mutation"
tests:
- name: status_code_is_201
assert: ${{ response.status_code == 201 }}
- name: documentation_created
assert: ${{ response.json()["security_test_category"] == "side_effects_prevention" }}

# Test that safe operations still work after unsafe documentation
- name: test_safe_operations_still_work
method: post
body:
title: "Safe Operations Verification"
code: "# Verify safe operations work"
language: "python"
# These should continue to work fine
safe_math: ${{ math.sqrt(25) }}
safe_datetime: ${{ datetime.datetime.now().year }}
safe_string: ${{ "test".upper() }}
safe_list: ${{ [1, 2, 3][1] }}
safe_dict: ${{ {"key": "value"}["key"] }}
tests:
- name: status_code_is_201
assert: ${{ response.status_code == 201 }}
- name: safe_math_works
assert: ${{ response.json()["safe_math"] == 5.0 }}
- name: safe_datetime_works
assert: ${{ response.json()["safe_datetime"] >= 2024 }}
- name: safe_string_works
assert: ${{ response.json()["safe_string"] == "TEST" }}
- name: safe_list_works
assert: ${{ response.json()["safe_list"] == 2 }}
- name: safe_dict_works
assert: ${{ response.json()["safe_dict"] == "value" }}
Loading