Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@

# Testing C/C++ for Security and Reliability
Building robust C/C++ applications is a highly challenging endeavor that requires thorough testing. While C/C++ enables us to write high-performance code, the memory-unsafety nature of the language brings a broad spectrum of security risks. Memory corruption issues constitute the vast majority of bugs and security vulnerabilities found in C/C++ projects, and their impact is best demonstrated by the [Heartbleed](https://en.wikipedia.org/wiki/Heartbleed) bug on OpenSSL. Regular unit and integration tests are essential to test that our code functions correctly - they are not enough to uncover memory-corruption bugs. (Whitebox and smart) Fuzz testing on the other hand, has established itself as the best practical method to find these issues in large code bases such as Google Chrome.

These examples require libssl-dev and libzstd-dev installed on Ubuntu. To install both dependencies you can run:
```sh
sudo apt install libssl-dev libzstd-dev -y
```
If you do not want to install both packages, you can use a devcontainer to run the examples, or comment out the include of the simple_examples folder in the main [CMakeLists.txt](CMakeLists.txt#L23) file.

Expand Down
71 changes: 71 additions & 0 deletions src/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import os
import pickle
import sqlite3
import yaml
from flask import Flask, request

app = Flask(__name__)

# --- VULN 1: Hard-coded secret ---
API_KEY = "SUPER_SECRET_API_KEY_12345" # Snyk should flag this


# --- VULN 2: SQL Injection ---
def get_user_by_name(username):
conn = sqlite3.connect("test.db")
cursor = conn.cursor()
# Intentionally vulnerable query
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)
result = cursor.fetchall()
conn.close()
return result


@app.route("/user")
def user():
username = request.args.get("username", "test")
data = get_user_by_name(username)
return {"data": str(data)}


# --- VULN 3: Command Injection ---
@app.route("/ping")
def ping():
ip = request.args.get("ip", "127.0.0.1")
# Intentionally dangerous: using user input in shell command
os.system(f"ping -c 1 {ip}")
return {"status": "ok"}


# --- VULN 4: Insecure Deserialization ---
@app.route("/load")
def load():
raw = request.args.get("data", None)
if not raw:
return {"error": "no data"}, 400

# Intentionally insecure: untrusted pickle.loads
obj = pickle.loads(bytes.fromhex(raw))
return {"loaded": str(obj)}


# --- VULN 5: Unsafe YAML load ---
@app.route("/yaml")
def yaml_load():
data = request.args.get("data", "a: 1")
# Unsafe loader (yaml.load instead of safe_load)
loaded = yaml.load(data, Loader=yaml.Loader) # vulnerable usage
return {"parsed": str(loaded)}


if __name__ == "__main__":
# Simple DB init to avoid runtime errors
conn = sqlite3.connect("test.db")
c = conn.cursor()
c.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT)")
c.execute("INSERT OR IGNORE INTO users (id, username) VALUES (1, 'test')")
conn.commit()
conn.close()

app.run(debug=True)
12 changes: 11 additions & 1 deletion src/state_example/crypto/crypto_1.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,22 @@ static crypto_nonce *current_nonce = 0;

void crypto_init() {
memset(&current_key, 0, sizeof(current_key));
if (current_nonce != 0) {
if (current_nonce != NULL) {
free(current_nonce);
current_nonce = NULL; // FIX: Prevent double free/use-after-free
}
current_nonce = calloc(1, sizeof(crypto_nonce));
if (current_nonce == NULL) {
// Handle allocation failure securely
// Optionally set an error state or abort
// For example:
// fprintf(stderr, "crypto_init: calloc failed\n");
// abort();
return;
}
current_state = initialized;
}
// FIX EXPLANATION: After freeing 'current_nonce', we immediately set it to NULL to prevent accidental double free or use-after-free. We also check the result of 'calloc' for allocation failure, which is a best practice for robust and secure C code.

enum crypto_state crypto_get_state() { return current_state; }

Expand Down