Skip to content

Code Security Guidelines

Faris Ansari edited this page Mar 19, 2021 · 12 revisions

Prefer query builder over raw SQL

If you are writing simple SQL queries there is a high chance it can be achieved using the Frappe Query Builder.

Don't use frappe.db.sql for simple queries like this:

result = frappe.db.sql('select name, title, description from tabToDo where owner = "john@example.com"')

Use frappe.db.get_all instead:

result = frappe.db.get_all('ToDo', fields=['name', 'title', 'description'], filters={'owner': 'john@example.com'})

Read the full API documentation.

Make your raw SQLs injection proof

If there are scenarios that you have to write raw SQL queries, make sure to account for SQL injections via user input that may be passed in a web request.

Don't use .format to substitute variables.

result = frappe.db.sql('select first_name from tabUser where name='{}'.format(user))

Pass variables to the sql method as a second parameter and they will be automatically sanitised and quoted.

result = frappe.db.sql('select first_name from tabUser where name=%s', [user])

If for some reason, you have to use .format to build your queries, make sure to sanitise your variables using frappe.db.escape.

result = frappe.db.sql('select first_name from tabUser where name={}'.format(frappe.db.escape(user)))

Don't allow creation of arbitrary documents via web request

Let's say you have created an API method create_document:

api.py

@frappe.whitelist()
def create_document(values):
  doc = frappe.get_doc(values).insert(ignore_permissions=True)
  return doc

This looks like a simple helper at first, but it allows a user to create any document on the site, since it bypasses the permissions check. Make sure to add additional checks if you really want arbitrary document creation.

You can use a combination of frappe.only_for method to restrict the method to System Managers and some manual checks. For e.g.,

def create_document(values):
  frappe.only_for('System User')
  
  if values['doctype'] not in ('ToDo', 'Note', 'Task'):
    frappe.throw('Invalid Document Type')

  doc = frappe.get_doc(values).insert(ignore_permissions=True)
  return doc
Clone this wiki locally