summaryrefslogtreecommitdiff
path: root/examples/secure_with_audit.py
diff options
context:
space:
mode:
authorIwan Aucamp <aucampia@gmail.com>2023-03-16 20:56:48 +0100
committerGitHub <noreply@github.com>2023-03-16 20:56:48 +0100
commit1c256765ac7d5e7327695a44269be09e51bd88b1 (patch)
tree903b6210b1d4601f3f08c6edd04458062b886ec0 /examples/secure_with_audit.py
parent60d98dbbf781371efd74de7d525443a11daa622e (diff)
downloadrdflib-1c256765ac7d5e7327695a44269be09e51bd88b1.tar.gz
docs: document avaiable security measures (#2270)
docs: document available security measures Several security measures can be used to mitigate risk when processing potentially malicious input. This change adds documentation about available security measures and examples and tests that illustrate their usage.
Diffstat (limited to 'examples/secure_with_audit.py')
-rw-r--r--examples/secure_with_audit.py120
1 files changed, 120 insertions, 0 deletions
diff --git a/examples/secure_with_audit.py b/examples/secure_with_audit.py
new file mode 100644
index 00000000..434be5a4
--- /dev/null
+++ b/examples/secure_with_audit.py
@@ -0,0 +1,120 @@
+"""
+This example demonstrates how to use `Python audit hooks
+<https://docs.python.org/3/library/sys.html#sys.addaudithook>`_ to block access
+to files and URLs.
+
+It installs a audit hook with `sys.addaudithook <https://docs.python.org/3/library/sys.html#sys.addaudithook>`_ that blocks access to files and
+URLs that end with ``blocked.jsonld``.
+
+The code in the example then verifies that the audit hook is blocking access to
+URLs and files as expected.
+"""
+
+import logging
+import os
+import sys
+from typing import Any, Optional, Tuple
+
+from rdflib import Graph
+
+
+def audit_hook(name: str, args: Tuple[Any, ...]) -> None:
+ """
+ An audit hook that blocks access when an attempt is made to open a
+ file or URL that ends with ``blocked.jsonld``.
+
+ Details of the audit events can be seen in the `audit events
+ table <https://docs.python.org/3/library/audit_events.html>`_.
+
+ :param name: The name of the audit event.
+ :param args: The arguments of the audit event.
+ :return: `None` if the audit hook does not block access.
+ :raises PermissionError: If the file or URL being accessed ends with ``blocked.jsonld``.
+ """
+ if name == "urllib.Request" and args[0].endswith("blocked.jsonld"):
+ raise PermissionError("Permission denied for URL")
+ if name == "open" and args[0].endswith("blocked.jsonld"):
+ raise PermissionError("Permission denied for file")
+ return None
+
+
+def main() -> None:
+ """
+ The main code of the example.
+
+ The important steps are:
+
+ * Install a custom audit hook that blocks some URLs and files.
+ * Attempt to parse a JSON-LD document that will result in a blocked URL being accessed.
+ * Verify that the audit hook blocked access to the URL.
+ * Attempt to parse a JSON-LD document that will result in a blocked file being accessed.
+ * Verify that the audit hook blocked access to the file.
+ """
+
+ logging.basicConfig(
+ level=os.environ.get("PYTHON_LOGGING_LEVEL", logging.INFO),
+ stream=sys.stderr,
+ datefmt="%Y-%m-%dT%H:%M:%S",
+ format=(
+ "%(asctime)s.%(msecs)03d %(process)d %(thread)d %(levelno)03d:%(levelname)-8s "
+ "%(name)-12s %(module)s:%(lineno)s:%(funcName)s %(message)s"
+ ),
+ )
+
+ if sys.version_info < (3, 8):
+ logging.warn("This example requires Python 3.8 or higher")
+ return None
+
+ # Install the audit hook
+ #
+ # note on type error: This is needed because we are running mypy with python
+ # 3.7 mode, so mypy thinks the previous condition will always be true.
+ sys.addaudithook(audit_hook) # type: ignore[unreachable]
+
+ graph = Graph()
+
+ # Attempt to parse a JSON-LD document that will result in the blocked URL
+ # being accessed.
+ error: Optional[PermissionError] = None
+ try:
+ graph.parse(
+ data=r"""{
+ "@context": "http://example.org/blocked.jsonld",
+ "@id": "example:subject",
+ "example:predicate": { "@id": "example:object" }
+ }""",
+ format="json-ld",
+ )
+ except PermissionError as caught:
+ logging.info("Permission denied: %s", caught)
+ error = caught
+
+ # `Graph.parse` would have resulted in a `PermissionError` being raised from
+ # the audit hook.
+ assert isinstance(error, PermissionError)
+ assert error.args[0] == "Permission denied for URL"
+
+ # Attempt to parse a JSON-LD document that will result in the blocked file
+ # being accessed.
+ error = None
+ try:
+ graph.parse(
+ data=r"""{
+ "@context": "file:///srv/blocked.jsonld",
+ "@id": "example:subject",
+ "example:predicate": { "@id": "example:object" }
+ }""",
+ format="json-ld",
+ )
+ except PermissionError as caught:
+ logging.info("Permission denied: %s", caught)
+ error = caught
+
+ # `Graph.parse` would have resulted in a `PermissionError` being raised from
+ # the audit hook.
+ assert isinstance(error, PermissionError)
+ assert error.args[0] == "Permission denied for file"
+
+
+if __name__ == "__main__":
+ main()