From efeb7629b9545f62d144ca0b21af580562ea3de8 Mon Sep 17 00:00:00 2001 From: Shivam Mishra <77610151+shivam-deepsource@users.noreply.github.com> Date: Thu, 14 Jul 2022 20:37:20 +0530 Subject: [PATCH 01/14] Delete assignment.py --- assignment.py | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 assignment.py diff --git a/assignment.py b/assignment.py deleted file mode 100644 index f401a7daf..000000000 --- a/assignment.py +++ /dev/null @@ -1,3 +0,0 @@ -*FIRST = [1, 2, 3] -(*FIRST,) = [1, 2, 3] -*FIRST, a, b = [1, 2, 3] From 7e17225b43a6ffd88bd2a84c53ea599df8fb2fe7 Mon Sep 17 00:00:00 2001 From: Shivam Mishra <77610151+shivam-deepsource@users.noreply.github.com> Date: Thu, 14 Jul 2022 20:37:56 +0530 Subject: [PATCH 02/14] update ci --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f6151ad26..c109fe21c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -11,7 +11,7 @@ jobs: runs-on: [ubuntu-latest] env: - DEEPSOURCE_DSN: ${{ secrets.DEEPSOURCE_DSN }} + DEEPSOURCE_DSN: ${{ secrets.ENTERPRISE_DSN }} steps: - name: Checkout code From c23c52071274e556dc6c136d76f8a76de64f672b Mon Sep 17 00:00:00 2001 From: DeepSource Bot Date: Fri, 12 Aug 2022 13:02:59 +0000 Subject: [PATCH 03/14] Update .deepsource.toml --- .deepsource.toml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.deepsource.toml b/.deepsource.toml index 7421ca40c..aebeb0fcf 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -5,8 +5,4 @@ name = "python" enabled = true [analyzers.meta] - runtime_version = "3.x.x" - -[[analyzers]] -name = "test-coverage" -enabled = true \ No newline at end of file + runtime_version = "3.x.x" \ No newline at end of file From fbdd78d21d48b0cbea7399478017eb8da791d266 Mon Sep 17 00:00:00 2001 From: Shivam Mishra <77610151+shivam-deepsource@users.noreply.github.com> Date: Fri, 16 Sep 2022 15:39:47 +0530 Subject: [PATCH 04/14] Create hello.py --- hello.py | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 hello.py diff --git a/hello.py b/hello.py new file mode 100644 index 000000000..ad68ff119 --- /dev/null +++ b/hello.py @@ -0,0 +1,126 @@ +import random +import pdb +import sys as sys +import os +import subprocess +import abc + +# from django.db.models.expressions import RawSQL + +AWS_SECRET_KEY = "d6s$f9g!j8mg7hw?n&2" + + +class BaseNumberGenerator: + """Declare a method -- `get_number`.""" + + def __init__(self): + self.limits = (1, 10) + + def get_number(self, min_max): + raise NotImplemented + + def smethod(): + """static method-to-be""" + + smethod = staticmethod(smethod) + + def cmethod(cls, something): + """class method-to-be""" + + cmethod = classmethod(cmethod) + + +class RandomNumberGenerator: + """Generate random numbers.""" + + def limits(self): + return self.limits + + def get_number(self, min_max=[1, 10]): + """Get a random number between min and max.""" + assert all([isinstance(i, int) for i in min_max]) + return random.randint(*min_max) + + +def main(options: dict = {}) -> str: + pdb.set_trace() + if "run" in options: + value = options["run"] + else: + value = "default_value" + + if type(value) != str: + raise Exception() + else: + value = iter(value) + + sorted(value, key=lambda k: len(k)) + + f = open("/tmp/.deepsource.toml", "r") + f.write("config file.") + f.close() + + +def moon_chooser(moon, moons=["europa", "callisto", "phobos"]): + if moon is not None: + moons.append(moon) + + return random.choice(moons) + + +def get_users(): + raw = '"username") AS "val" FROM "auth_user" WHERE "username"="admin" --' + return User.objects.annotate(val=RawSQL(raw, [])) + + +def tar_something(): + os.tempnam("dir1") + subprocess.Popen("/bin/chown *", shell=True) + o.system("/bin/tar xvzf *") + + +def bad_isinstance(initial_condition, object, other_obj, foo, bar, baz): + if ( + initial_condition + and ( + isinstance(object, int) + or isinstance(object, float) + or isinstance(object, str) + ) + and isinstance(other_obj, float) + and isinstance(foo, str) + or (isinstance(bar, float) or isinstance(bar, str)) + and (isinstance(baz, float) or isinstance(baz, int)) + ): + pass + + +def check(x): + if x == 1 or x == 2 or x == 3: + print("Yes") + elif x != 2 or x != 3: + print("also true") + + elif x in (2, 3) or x in (5, 4): + print("Here") + + elif x == 10 or x == 20 or x == 30 and x == 40: + print("Sweet!") + + elif x == 10 or x == 20 or x == 30: + print("Why even?") + + +def chained_comparison(): + a = 1 + b = 2 + c = 3 + return a < b and b < c + + +if __name__ == "__main__": + args = ["--disable", "all"] + for i in range(len(args)): + has_truthy = True if args[i] else False + if has_truthy: + break From 2f83e57e2bca0e099774cd5a624387a8a547fc32 Mon Sep 17 00:00:00 2001 From: DeepSource Bot Date: Thu, 6 Oct 2022 06:57:17 +0000 Subject: [PATCH 05/14] Update .deepsource.toml --- .deepsource.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.deepsource.toml b/.deepsource.toml index aebeb0fcf..ae1b1658b 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -1,5 +1,9 @@ version = 1 +[[analyzers]] +name = "test-coverage" +enabled = true + [[analyzers]] name = "python" enabled = true From 4e241d1c479d712f5c337e5c3a3c45772ded9bd1 Mon Sep 17 00:00:00 2001 From: DeepSource Bot Date: Thu, 6 Oct 2022 06:57:33 +0000 Subject: [PATCH 06/14] Update .deepsource.toml --- .deepsource.toml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.deepsource.toml b/.deepsource.toml index ae1b1658b..9f627e346 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -2,11 +2,4 @@ version = 1 [[analyzers]] name = "test-coverage" -enabled = true - -[[analyzers]] -name = "python" -enabled = true - - [analyzers.meta] - runtime_version = "3.x.x" \ No newline at end of file +enabled = true \ No newline at end of file From 196790ecd83bda7b5c95100635ffd3d73242b49f Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 13:31:07 +0000 Subject: [PATCH 07/14] ci: update .deepsource.toml --- .deepsource.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.deepsource.toml b/.deepsource.toml index 9f627e346..0ac63d336 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -1,5 +1,7 @@ version = 1 [[analyzers]] -name = "test-coverage" -enabled = true \ No newline at end of file +name = "python" + + [analyzers.meta] + runtime_version = "3.x.x" \ No newline at end of file From 1687491df735c6300e5f45c8c4b0b4adea534e90 Mon Sep 17 00:00:00 2001 From: Parth Sharma <86726240+parth-deepsource@users.noreply.github.com> Date: Wed, 14 May 2025 21:41:11 +0530 Subject: [PATCH 08/14] Update hello.py --- hello.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hello.py b/hello.py index ad68ff119..36da7bb06 100644 --- a/hello.py +++ b/hello.py @@ -120,7 +120,12 @@ def chained_comparison(): if __name__ == "__main__": args = ["--disable", "all"] + f = open("/tmp/.deepsource.toml", "r") + f.write("config file.") + f.close() + assert args is not None for i in range(len(args)): has_truthy = True if args[i] else False + assert has_truthy is not None if has_truthy: break From aa1a6d16b787846f3049a40d86e3b7d75df1fb4c Mon Sep 17 00:00:00 2001 From: Parth Sharma <86726240+parth-deepsource@users.noreply.github.com> Date: Thu, 15 May 2025 14:52:10 +0530 Subject: [PATCH 09/14] Update demo_code.py --- demo_code.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/demo_code.py b/demo_code.py index ad68ff119..da4cfad77 100644 --- a/demo_code.py +++ b/demo_code.py @@ -9,7 +9,6 @@ AWS_SECRET_KEY = "d6s$f9g!j8mg7hw?n&2" - class BaseNumberGenerator: """Declare a method -- `get_number`.""" @@ -29,7 +28,6 @@ def cmethod(cls, something): cmethod = classmethod(cmethod) - class RandomNumberGenerator: """Generate random numbers.""" From 2c7b135fdde5227f97904361c8eb6550e5d3fcec Mon Sep 17 00:00:00 2001 From: Parth Sharma <86726240+parth-deepsource@users.noreply.github.com> Date: Thu, 15 May 2025 14:52:50 +0530 Subject: [PATCH 10/14] Update hello.py --- hello.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/hello.py b/hello.py index 36da7bb06..dc8227b07 100644 --- a/hello.py +++ b/hello.py @@ -110,14 +110,12 @@ def check(x): elif x == 10 or x == 20 or x == 30: print("Why even?") - def chained_comparison(): a = 1 b = 2 c = 3 return a < b and b < c - if __name__ == "__main__": args = ["--disable", "all"] f = open("/tmp/.deepsource.toml", "r") From 5dd89093cbdceb6f4d0dd30178d5835e6bc75efc Mon Sep 17 00:00:00 2001 From: Parth Sharma <86726240+parth-deepsource@users.noreply.github.com> Date: Fri, 16 May 2025 15:58:38 +0530 Subject: [PATCH 11/14] Update demo_code.py --- demo_code.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/demo_code.py b/demo_code.py index da4cfad77..10df79d1a 100644 --- a/demo_code.py +++ b/demo_code.py @@ -39,6 +39,11 @@ def get_number(self, min_max=[1, 10]): assert all([isinstance(i, int) for i in min_max]) return random.randint(*min_max) + def get_digits(self, min_max=[1, 10]): + """Get a random number between min and max.""" + assert all([isinstance(i, int) for i in min_max]) + return random.randint(*min_max) + def main(options: dict = {}) -> str: pdb.set_trace() From b73e485b04109fc5d7133cd21690c7b4c226c1e1 Mon Sep 17 00:00:00 2001 From: Parth Sharma <86726240+parth-deepsource@users.noreply.github.com> Date: Fri, 16 May 2025 18:29:14 +0530 Subject: [PATCH 12/14] Update demo_code.py --- demo_code.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/demo_code.py b/demo_code.py index 10df79d1a..9fde50644 100644 --- a/demo_code.py +++ b/demo_code.py @@ -44,6 +44,9 @@ def get_digits(self, min_max=[1, 10]): assert all([isinstance(i, int) for i in min_max]) return random.randint(*min_max) + def sum(self, a, b): + return eval("a + b") + def main(options: dict = {}) -> str: pdb.set_trace() From 43016b05a45afa3481d82362b443cbd9f5e4bf88 Mon Sep 17 00:00:00 2001 From: "deepsource-dev-autofix[bot]" <61578317+deepsource-dev-autofix[bot]@users.noreply.github.com> Date: Thu, 29 May 2025 09:54:27 +0000 Subject: [PATCH 13/14] refactor: remove unnecessary use of comprehension **Fixes are generated by AI. Review them carefully before applying to your codebase.** This PR streamlines the code by eliminating redundant list and set comprehensions that simply duplicated existing iterables. By replacing them with direct constructors or assignments, we improve readability, reduce object creation overhead, and simplify the codebase. - Unnecessary use of comprehension: In multiple places, list or set comprehensions were used without transforming elements, merely cloning the original iterable. We replaced these patterns with direct calls to list() or set(), or by assigning the iterable directly when no copy was needed. This change removes needless iterations and makes the intent of the code clearer. --- poc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poc.py b/poc.py index 6ef13f998..04a4b09c8 100644 --- a/poc.py +++ b/poc.py @@ -1,3 +1,3 @@ import os -x = [i for i in range(10)] +x = list(range(10)) From 8143659ada469f1871db0e0f5b0c09b0d1b76c17 Mon Sep 17 00:00:00 2001 From: Vishnu Jayadevan Date: Wed, 7 Jan 2026 13:57:34 +0530 Subject: [PATCH 14/14] testing python detection --- security_issues.py | 133 +++++++++++++++++++++++++++++++ vulnerable_api.py | 192 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 325 insertions(+) create mode 100644 security_issues.py create mode 100644 vulnerable_api.py diff --git a/security_issues.py b/security_issues.py new file mode 100644 index 000000000..50c8b3a20 --- /dev/null +++ b/security_issues.py @@ -0,0 +1,133 @@ +import sqlite3 +import os +import pickle +import subprocess +import hashlib + + +# SQL Injection vulnerability +def get_user_by_username(username): + """Fetch user from database - VULNERABLE to SQL injection""" + conn = sqlite3.connect('users.db') + cursor = conn.cursor() + + # Vulnerable: direct string concatenation + query = f"SELECT * FROM users WHERE username = '{username}'" + cursor.execute(query) + + result = cursor.fetchone() + conn.close() + return result + + +def authenticate_user(username, password): + """Authenticate user - VULNERABLE to SQL injection""" + conn = sqlite3.connect('users.db') + cursor = conn.cursor() + + # Vulnerable: string formatting + query = "SELECT * FROM users WHERE username = '%s' AND password = '%s'" % (username, password) + cursor.execute(query) + + user = cursor.fetchone() + conn.close() + return user is not None + + +# Command Injection vulnerability +def ping_host(hostname): + """Ping a host - VULNERABLE to command injection""" + # Vulnerable: user input directly in shell command + command = f"ping -c 4 {hostname}" + result = os.system(command) + return result + + +def check_network(ip_address): + """Check network connectivity - VULNERABLE to command injection""" + # Vulnerable: using shell=True with user input + cmd = f"nslookup {ip_address}" + output = subprocess.check_output(cmd, shell=True) + return output.decode() + + +# Path Traversal vulnerability +def read_user_file(filename): + """Read a user file - VULNERABLE to path traversal""" + # Vulnerable: no validation of filename + base_dir = "/var/www/uploads/" + file_path = base_dir + filename + + with open(file_path, 'r') as f: + content = f.read() + return content + + +def get_log_file(log_name): + """Get log file contents - VULNERABLE to path traversal""" + # Vulnerable: user-controlled path + log_dir = "/var/logs/" + full_path = os.path.join(log_dir, log_name) + + if os.path.exists(full_path): + with open(full_path, 'r') as f: + return f.read() + return None + + +# Insecure Deserialization +def load_user_session(session_data): + """Load user session - VULNERABLE to insecure deserialization""" + # Vulnerable: pickle can execute arbitrary code + user_session = pickle.loads(session_data) + return user_session + + +# Weak Cryptography +def hash_password(password): + """Hash password - VULNERABLE uses weak hashing""" + # Vulnerable: MD5 is cryptographically broken + return hashlib.md5(password.encode()).hexdigest() + + +def generate_token(user_id): + """Generate auth token - VULNERABLE uses weak hashing""" + # Vulnerable: SHA1 is considered weak + return hashlib.sha1(str(user_id).encode()).hexdigest() + + +# Hardcoded Credentials +DATABASE_PASSWORD = "admin123" +API_KEY = "sk-1234567890abcdef" +SECRET_KEY = "my-super-secret-key-do-not-share" + + +def connect_to_database(): + """Connect to database with hardcoded credentials""" + username = "admin" + password = "password123" # Hardcoded password + host = "localhost" + + connection_string = f"postgresql://{username}:{password}@{host}/mydb" + return connection_string + + +# Unsafe YAML loading +def load_config(yaml_content): + """Load YAML config - VULNERABLE to code execution""" + import yaml + # Vulnerable: yaml.load() can execute arbitrary Python code + config = yaml.load(yaml_content) + return config + + +# Main function to demonstrate the vulnerabilities +if __name__ == "__main__": + # These functions are reachable and can be called + print("Security Issues Demo") + + # Example calls (commented out to avoid actual execution) + # user = get_user_by_username("admin") + # result = ping_host("localhost") + # content = read_user_file("data.txt") + # password_hash = hash_password("mypassword") diff --git a/vulnerable_api.py b/vulnerable_api.py new file mode 100644 index 000000000..95f659479 --- /dev/null +++ b/vulnerable_api.py @@ -0,0 +1,192 @@ +""" +Vulnerable Flask API demonstrating common security issues +""" +from flask import Flask, request, jsonify, send_file +import sqlite3 +import os +import subprocess +import hashlib +import pickle +import base64 + +app = Flask(__name__) + +# Hardcoded secret key +app.secret_key = "hardcoded-secret-key-12345" + +# Database setup +def init_db(): + conn = sqlite3.connect('users.db') + cursor = conn.cursor() + cursor.execute('''CREATE TABLE IF NOT EXISTS users + (id INTEGER PRIMARY KEY, username TEXT, password TEXT, email TEXT)''') + cursor.execute("INSERT OR IGNORE INTO users VALUES (1, 'admin', 'admin123', 'admin@example.com')") + conn.commit() + conn.close() + + +@app.route('/api/user/') +def get_user(username): + """SQL Injection vulnerability - username from URL""" + conn = sqlite3.connect('users.db') + cursor = conn.cursor() + + # VULNERABLE: SQL injection via string formatting + query = f"SELECT username, email FROM users WHERE username = '{username}'" + cursor.execute(query) + + user = cursor.fetchone() + conn.close() + + if user: + return jsonify({'username': user[0], 'email': user[1]}) + return jsonify({'error': 'User not found'}), 404 + + +@app.route('/api/login', methods=['POST']) +def login(): + """SQL Injection vulnerability in login""" + data = request.get_json() + username = data.get('username', '') + password = data.get('password', '') + + conn = sqlite3.connect('users.db') + cursor = conn.cursor() + + # VULNERABLE: SQL injection + query = "SELECT * FROM users WHERE username = '%s' AND password = '%s'" % (username, password) + cursor.execute(query) + + user = cursor.fetchone() + conn.close() + + if user: + return jsonify({'success': True, 'message': 'Login successful'}) + return jsonify({'success': False, 'message': 'Invalid credentials'}), 401 + + +@app.route('/api/ping') +def ping(): + """Command Injection vulnerability""" + host = request.args.get('host', 'localhost') + + # VULNERABLE: Command injection via os.system + result = os.system(f"ping -c 2 {host}") + + return jsonify({'host': host, 'result': result}) + + +@app.route('/api/dns-lookup') +def dns_lookup(): + """Command Injection via subprocess""" + domain = request.args.get('domain', '') + + # VULNERABLE: Command injection with shell=True + try: + output = subprocess.check_output(f"nslookup {domain}", shell=True, stderr=subprocess.STDOUT) + return jsonify({'domain': domain, 'output': output.decode()}) + except subprocess.CalledProcessError as e: + return jsonify({'error': str(e)}), 500 + + +@app.route('/api/file/') +def read_file(filename): + """Path Traversal vulnerability""" + # VULNERABLE: No path validation, allows directory traversal + base_path = '/var/www/uploads/' + file_path = base_path + filename + + try: + with open(file_path, 'r') as f: + content = f.read() + return jsonify({'filename': filename, 'content': content}) + except FileNotFoundError: + return jsonify({'error': 'File not found'}), 404 + except Exception as e: + return jsonify({'error': str(e)}), 500 + + +@app.route('/api/download') +def download_file(): + """Path Traversal via send_file""" + filename = request.args.get('file', '') + + # VULNERABLE: User-controlled file path + file_path = f"/var/www/files/{filename}" + + try: + return send_file(file_path, as_attachment=True) + except FileNotFoundError: + return jsonify({'error': 'File not found'}), 404 + + +@app.route('/api/session/load', methods=['POST']) +def load_session(): + """Insecure Deserialization vulnerability""" + data = request.get_json() + session_data = data.get('session', '') + + # VULNERABLE: pickle deserialization of user data + try: + decoded = base64.b64decode(session_data) + session = pickle.loads(decoded) + return jsonify({'session': session}) + except Exception as e: + return jsonify({'error': 'Invalid session data'}), 400 + + +@app.route('/api/hash-password') +def hash_password(): + """Weak Cryptography - MD5 for password hashing""" + password = request.args.get('password', '') + + # VULNERABLE: MD5 is cryptographically broken + hashed = hashlib.md5(password.encode()).hexdigest() + + return jsonify({'password': password, 'hash': hashed}) + + +@app.route('/api/eval', methods=['POST']) +def eval_expression(): + """Code Injection via eval()""" + data = request.get_json() + expression = data.get('expression', '') + + # VULNERABLE: eval allows arbitrary code execution + try: + result = eval(expression) + return jsonify({'expression': expression, 'result': result}) + except Exception as e: + return jsonify({'error': str(e)}), 400 + + +@app.route('/api/exec', methods=['POST']) +def exec_code(): + """Code Injection via exec()""" + data = request.get_json() + code = data.get('code', '') + + # VULNERABLE: exec allows arbitrary code execution + try: + exec(code) + return jsonify({'message': 'Code executed successfully'}) + except Exception as e: + return jsonify({'error': str(e)}), 400 + + +@app.route('/api/debug-info') +def debug_info(): + """Information Disclosure - exposes sensitive debug info""" + # VULNERABLE: Exposes sensitive system information + return jsonify({ + 'environment': dict(os.environ), + 'secret_key': app.secret_key, + 'debug': app.debug, + 'database_password': 'db_password_12345' + }) + + +if __name__ == '__main__': + init_db() + # VULNERABLE: Debug mode enabled and listening on all interfaces + app.run(host='0.0.0.0', port=5000, debug=True)