From d63d04a5b8815c5d41f51ca45c2aa1d98e140668 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 4 Jul 2024 18:39:41 +0200 Subject: [PATCH] feat: add doctype caster security rule --- rules/security.py | 1 + rules/security.yml | 31 +++++++++++++++++++++++++++++++ rules/some/doctype/security.py | 25 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 rules/some/doctype/security.py diff --git a/rules/security.py b/rules/security.py index f477d7c..8afd530 100644 --- a/rules/security.py +++ b/rules/security.py @@ -4,3 +4,4 @@ def function_name(input): # ok: frappe-codeinjection-eval eval("1 + 1") + diff --git a/rules/security.yml b/rules/security.yml index 8b21979..f606643 100644 --- a/rules/security.yml +++ b/rules/security.yml @@ -8,3 +8,34 @@ rules: dynamic content. Avoid it or use safe_eval(). languages: [python] severity: ERROR + +- id: require-permission-decorator-on-conversion-methods + patterns: + - pattern-inside: | + class $CLASS(...): + ... + - pattern: | + def $CONVERSION_METHOD(...): + ... + - pattern-not: | + @requires_permission(...) + def $CONVERSION_METHOD(...): + ... + - pattern-not: | + @frappe.requires_permission(...) + def $CONVERSION_METHOD(...): + ... + - metavariable-regex: + metavariable: '$CONVERSION_METHOD' + regex: '^_(from|into)_(.*)$' + + message: | + Conversion method '$CONVERSION_METHOD' of class '$CLASS' spans the security boundary between two doctypes. + It therefore must declare its security context exiplictly and upfront. + Do this by setting at least one explicit @frappe.requires_permission(, ) decorator on '$CONVERSION_METHOD'. + languages: [python] + severity: ERROR + paths: + include: + - "*/**/doctype/*" + diff --git a/rules/some/doctype/security.py b/rules/some/doctype/security.py new file mode 100644 index 0000000..b1f8ed4 --- /dev/null +++ b/rules/some/doctype/security.py @@ -0,0 +1,25 @@ +from frappe.model import Document +import frappe +from frappe import requires_permission + +# ruleid: require-permission-decorator-on-conversion-methods +class MyDocument(Document): + def _into_sales_invoice(self, so): + ... + +# ok: require-permission-decorator-on-conversion-methods +class MyDocument(Document): + @requires_permission("Sales Invoice", "create") + def _into_sales_invoice(self, so): + ... + +# ruleid: require-permission-decorator-on-conversion-methods +class MyDocument(Document): + def _from_sales_invoice(self, so): + ... + +# ok: require-permission-decorator-on-conversion-methods +class MyDocument(Document): + @frappe.requires_permission("Sales Invoice", "read") + def _from_sales_invoice(self, so): + ...