92 lines
2.9 KiB
Python
92 lines
2.9 KiB
Python
import json
|
|
import os
|
|
import tempfile
|
|
import unittest
|
|
from subprocess import run, PIPE
|
|
from textwrap import dedent
|
|
|
|
from odoo import tools
|
|
from odoo.tests.common import TransactionCase
|
|
|
|
try:
|
|
import pylint
|
|
except ImportError:
|
|
pylint = None
|
|
try:
|
|
pylint_bin = tools.which('pylint')
|
|
except IOError:
|
|
pylint_bin = None
|
|
|
|
HERE = os.path.dirname(os.path.realpath(__file__))
|
|
@unittest.skipUnless(pylint and pylint_bin, "testing lints requires pylint")
|
|
class TestSqlLint(TransactionCase):
|
|
def check(self, testtext):
|
|
with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', delete=False) as f:
|
|
self.addCleanup(os.remove, f.name)
|
|
f.write(dedent(testtext).strip())
|
|
|
|
result = run(
|
|
[pylint_bin,
|
|
f'--rcfile={os.devnull}',
|
|
'--load-plugins=_odoo_checker_sql_injection',
|
|
'--disable=all',
|
|
'--enable=sql-injection',
|
|
'--output-format=json',
|
|
f.name,
|
|
],
|
|
check=False,
|
|
stdout=PIPE, encoding='utf-8',
|
|
env={
|
|
**os.environ,
|
|
'PYTHONPATH': HERE+os.pathsep+os.environ.get('PYTHONPATH', ''),
|
|
}
|
|
)
|
|
return result.returncode, json.loads(result.stdout)
|
|
|
|
def test_printf(self):
|
|
r, [err] = self.check("""
|
|
def do_the_thing(cr, name):
|
|
cr.execute('select %s from thing' % name)
|
|
""")
|
|
self.assertTrue(r, "should have noticed the injection")
|
|
self.assertEqual(err['line'], 2, err)
|
|
|
|
r, errs = self.check("""
|
|
def do_the_thing(self):
|
|
self.env.cr.execute("select thing from %s" % self._table)
|
|
""")
|
|
self.assertFalse(r, f"underscore-attributes are allowed\n{errs}")
|
|
|
|
r, errs = self.check("""
|
|
def do_the_thing(self):
|
|
query = "select thing from %s"
|
|
self.env.cr.execute(query % self._table)
|
|
""")
|
|
self.assertFalse(r, f"underscore-attributes are allowed\n{errs}")
|
|
|
|
def test_fstring(self):
|
|
r, [err] = self.check("""
|
|
def do_the_thing(cr, name):
|
|
cr.execute(f'select {name} from thing')
|
|
""")
|
|
self.assertTrue(r, "should have noticed the injection")
|
|
self.assertEqual(err['line'], 2, err)
|
|
|
|
r, errs = self.check("""
|
|
def do_the_thing(cr, name):
|
|
cr.execute(f'select name from thing')
|
|
""")
|
|
self.assertFalse(r, f"unnecessary fstring should be innocuous\n{errs}")
|
|
|
|
r, errs = self.check("""
|
|
def do_the_thing(cr, name, value):
|
|
cr.execute(f'select {name} from thing where field = %s', [value])
|
|
""")
|
|
self.assertFalse(r, f"probably has a good reason for the extra arg\n{errs}")
|
|
|
|
r, errs = self.check("""
|
|
def do_the_thing(self):
|
|
self.env.cr.execute(f'select name from {self._table}')
|
|
""")
|
|
self.assertFalse(r, f'underscore-attributes are allowable\n{errs}')
|