Code Guard rules
Code Guard has 38 rules in four groups. Each one shows the code an agent wrote that Code Guard stops (Flagged) and the code it asks for instead (Fixed). Rules marked Free are in every plan; the Student, Pro and Team plans include them all. You can turn any rule off on the Code Guard page.
| Rule | Severity | Languages | Plan |
|---|---|---|---|
| Secret written into the code | Critical | Every file | Free |
| Memory from new[] is never freed | High | C++ | Free |
| Raw new without delete or a smart pointer | Medium | C++ | Paid plans |
| malloc without free | High | C, C++ | Free |
| gets() can always overflow | Critical | C, C++ | Free |
| strcpy or strcat without a size limit | High | C, C++ | Paid plans |
| sprintf without a size limit | High | C, C++ | Paid plans |
| scanf("%s") without a width | High | C, C++ | Paid plans |
| A variable used as the printf format | High | C, C++ | Paid plans |
| mem::forget leaks the value | Medium | Rust | Paid plans |
| Box::leak is never freed | Medium | Rust | Paid plans |
| SQL built by joining strings | Critical | Python, JavaScript / TypeScript, Java, C#, PHP, Go, Ruby, Kotlin | Free |
| SQL built with an f-string or % formatting | Critical | Python | Free |
| SQL built with a template string | Critical | JavaScript / TypeScript | Free |
| SQL built with fmt.Sprintf | Critical | Go | Free |
| Request data straight into SQL | Critical | PHP | Free |
| include or require with request data | Critical | PHP | Free |
| eval or exec | High | Python | Paid plans |
| eval or new Function | High | JavaScript / TypeScript, PHP | Paid plans |
| subprocess with shell=True or os.system | High | Python | Paid plans |
| Shell command built from a variable | High | JavaScript / TypeScript | Paid plans |
| Runtime.exec with a joined string | High | Java, Kotlin | Paid plans |
| system() runs a shell | Medium | C, C++ | Paid plans |
| pickle.loads on data | High | Python | Paid plans |
| yaml.load without SafeLoader | High | Python | Paid plans |
| innerHTML set from a variable | Medium | JavaScript / TypeScript | Paid plans |
| dangerouslySetInnerHTML without sanitizing | High | JavaScript / TypeScript | Paid plans |
| Certificate checks switched off | High | Python, JavaScript / TypeScript, Go | Paid plans |
| Every certificate or host name accepted | High | Java, Kotlin, C# | Paid plans |
| Permissions 777 | High | C, C++, Python, JavaScript / TypeScript, Go, Rust, Java, Kotlin, C#, PHP, Ruby, Shell | Paid plans |
| MD5 or SHA-1 for passwords | High | C, C++, Python, JavaScript / TypeScript, Go, Rust, Java, Kotlin, C#, PHP, Ruby, Shell | Paid plans |
| Predictable random numbers for a secret | Medium | Python, JavaScript / TypeScript, Go, Java, PHP, Ruby, C# | Paid plans |
| ECB encryption mode | High | C, C++, Python, JavaScript / TypeScript, Go, Rust, Java, Kotlin, C#, PHP, Ruby, Shell | Paid plans |
| JWT signature not checked | Critical | Python, JavaScript / TypeScript, Java, Go | Free |
| Flask debug mode on | High | Python | Paid plans |
| CORS open to every site | Medium | JavaScript / TypeScript, Python, Go, Java, PHP, C# | Paid plans |
| CSRF protection switched off | Medium | Python, Java, Kotlin | Paid plans |
| Downloaded script piped into a shell | Medium | Shell | Paid plans |
Secrets
Keys, tokens and passwords written into the code.
Secret written into the code
Critical
Why it matters. Anyone who can read the repository, its history or a build can use the key; deleting it later doesn't remove it from git history.
How to fix it. Read it from an environment variable or a secret manager (os.environ["API_KEY"], process.env.API_KEY) and keep the value out of the repository, for example in an ignored .env file.
API_SECRET = "q8Xz2LmN4vB7rT1wK9pY3sD6hJ5c"API_SECRET = os.environ["API_SECRET"]Memory
Leaks and buffer overflows in C, C++ and Rust.
Memory from new[] is never freed
High
Why it matters. The array is allocated with new[] but the file never calls delete[], so the memory leaks every time this runs.
How to fix it. Let an owner free it: std::vector<T>, or auto p = std::make_unique<T[]>(n). If you must keep raw new[], pair it with delete[].
void load() {
int* buffer = new int[1000];
fill(buffer);
}void load() {
auto buffer = std::make_unique<int[]>(1000);
fill(buffer.get());
}Raw new without delete or a smart pointer
Medium
Why it matters. An object created with new and kept in a raw pointer is freed only if every path reaches a delete; an early return or an exception leaks it.
How to fix it. Use std::make_unique<T>(...) (or std::make_shared) so the object is freed when its owner goes away (RAII).
Widget* w = new Widget(config);
w->run();auto w = std::make_unique<Widget>(config);
w->run();malloc without free
High
Why it matters. Memory from malloc, calloc or realloc is allocated but the file never calls free, so it leaks.
How to fix it. free() it on every path once you're done (a single cleanup label in C helps). In C++, use std::vector or std::unique_ptr instead.
char* name = malloc(64);
strncpy(name, input, 63);
return strlen(name);char* name = malloc(64);
strncpy(name, input, 63);
size_t n = strlen(name);
free(name);
return n;gets() can always overflow
Critical
Why it matters. gets() has no way to know the buffer's size, so any long enough input overwrites memory. It was removed from C11.
How to fix it. Use fgets(buffer, sizeof buffer, stdin), or std::getline in C++.
char line[128];
gets(line);char line[128];
fgets(line, sizeof line, stdin);strcpy or strcat without a size limit
High
Why it matters. strcpy and strcat copy until the source ends, whatever the destination's size, which overflows the buffer on long input.
How to fix it. Use snprintf(dst, sizeof dst, "%s", src) or strlcpy, or std::string in C++.
char path[64];
strcpy(path, user_input);char path[64];
snprintf(path, sizeof path, "%s", user_input);sprintf without a size limit
High
Why it matters. sprintf writes as much as the format produces, past the end of the buffer if it's too small.
How to fix it. Use snprintf(buffer, sizeof buffer, ...), or std::format / std::ostringstream in C++.
char msg[32];
sprintf(msg, "Hello %s", name);char msg[32];
snprintf(msg, sizeof msg, "Hello %s", name);scanf("%s") without a width
High
Why it matters. %s with no width reads a word of any length into a fixed buffer.
How to fix it. Give the width, one less than the buffer: scanf("%63s", name) for char name[64], or use fgets.
char name[64];
scanf("%s", name);char name[64];
scanf("%63s", name);A variable used as the printf format
High
Why it matters. If the text contains %n or %x, printf reads or writes memory it shouldn't (a format string attack).
How to fix it. Pass a fixed format: printf("%s", text).
printf(message);printf("%s", message);mem::forget leaks the value
Medium
Why it matters. std::mem::forget never runs the value's destructor, so the memory, file or lock it holds is never released.
How to fix it. Let the value drop. If it must outlive this scope, store it; if it crosses an FFI boundary, use ManuallyDrop or Box::into_raw and free it on the other side.
let file = File::open(path)?;
std::mem::forget(file);let file = File::open(path)?;
drop(file);Box::leak is never freed
Medium
Why it matters. Box::leak gives a 'static reference by never freeing the memory; in a loop or per request it grows without bound.
How to fix it. Keep the Box (or an Arc) and borrow from it. Use leak only for a value created once at startup.
let name: &'static str = Box::leak(input.into_boxed_str());let name: Arc<str> = Arc::from(input);Injection
Outside input reaching SQL, a shell, eval or the page's HTML.
SQL built by joining strings
Critical
Why it matters. Adding a value to the query text lets an attacker change the query: ' OR 1=1 -- reads every row (SQL injection).
How to fix it. Use placeholders and pass the values separately: db.query("SELECT * FROM users WHERE id = ?", [id]).
const rows = await db.query("SELECT * FROM users WHERE id = " + id);const rows = await db.query("SELECT * FROM users WHERE id = ?", [id]);SQL built with an f-string or % formatting
Critical
Why it matters. Formatting a value into the query text lets an attacker change the query (SQL injection).
How to fix it. Pass the values as parameters: cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)).
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))SQL built with a template string
Critical
Why it matters. A ${value} inside the query text lets an attacker change the query (SQL injection).
How to fix it. Use placeholders: db.query("SELECT * FROM users WHERE id = $1", [id]), or a tagged template your driver escapes (sql...).
await db.query(`SELECT * FROM users WHERE email = '${email}'`);await db.query("SELECT * FROM users WHERE email = $1", [email]);SQL built with fmt.Sprintf
Critical
Why it matters. Formatting a value into the query text lets an attacker change the query (SQL injection).
How to fix it. Use placeholders: db.Query("SELECT * FROM users WHERE id = $1", id).
rows, err := db.Query(fmt.Sprintf("SELECT * FROM users WHERE id = %s", id))rows, err := db.Query("SELECT * FROM users WHERE id = $1", id)Request data straight into SQL
Critical
Why it matters. $_GET, $_POST and $_REQUEST come from the visitor; putting them in a query lets them rewrite it (SQL injection).
How to fix it. Use a prepared statement: $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?'); $stmt->execute([$_GET['id']]);
$result = mysqli_query($db, "SELECT * FROM users WHERE id = " . $_GET['id']);$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$_GET['id']]);include or require with request data
Critical
Why it matters. Letting the visitor choose the file to include lets them run or read any file on the server.
How to fix it. Map the request value to a fixed list of allowed files, and include only from that list.
include($_GET['page'] . '.php');$pages = ['home' => 'home.php', 'about' => 'about.php'];
include($pages[$_GET['page']] ?? 'home.php');eval or exec
High
Why it matters. eval and exec run any Python they're given; with outside input that is remote code execution.
How to fix it. Parse the data instead: json.loads for JSON, ast.literal_eval for Python literals, a dict of allowed functions for commands.
value = eval(request.args["expr"])value = ast.literal_eval(request.args["expr"])eval or new Function
High
Why it matters. eval and new Function run any code they're given; with outside input that is code injection.
How to fix it. Parse the data instead (JSON.parse), or look the action up in an object of allowed functions.
const result = eval(userExpression);const result = JSON.parse(userInput);subprocess with shell=True or os.system
High
Why it matters. The command goes through the shell, so a value containing ; rm -rf ~ runs as a second command (command injection).
How to fix it. Pass a list and no shell: subprocess.run(["convert", path, out], check=True).
subprocess.run(f"convert {path} out.png", shell=True)subprocess.run(["convert", path, "out.png"], check=True)Shell command built from a variable
High
Why it matters. child_process.exec runs a shell, so a value containing ; rm -rf ~ runs as a second command (command injection).
How to fix it. Use execFile or spawn with an argument list: execFile("git", ["clone", url]).
exec(`git clone ${url}`);execFile("git", ["clone", url]);Runtime.exec with a joined string
High
Why it matters. Joining input into the command lets it add arguments or commands of its own.
How to fix it. Use new ProcessBuilder("git", "clone", url) with each argument separate.
Runtime.getRuntime().exec("ping " + host);new ProcessBuilder("ping", host).start();system() runs a shell
Medium
Why it matters. system() hands the string to a shell, so any input in it can add commands (command injection).
How to fix it. Run the program directly with execv / posix_spawn and separate arguments.
system(command);char* argv[] = {"ls", "-l", NULL};
posix_spawnp(&pid, "ls", NULL, NULL, argv, environ);pickle.loads on data
High
Why it matters. Unpickling runs code the data asks for; a crafted file or request is remote code execution.
How to fix it. Use json (or msgpack) for data from outside. Load pickles only from files you wrote yourself.
obj = pickle.loads(request.data)obj = json.loads(request.data)yaml.load without SafeLoader
High
Why it matters. yaml.load with the default loader can build any Python object, which runs code from the YAML.
How to fix it. Use yaml.safe_load(text), or yaml.load(text, Loader=yaml.SafeLoader).
config = yaml.load(text)config = yaml.safe_load(text)innerHTML set from a variable
Medium
Why it matters. Text put into innerHTML is parsed as HTML, so a value with <script> or onerror= runs in the page (XSS).
How to fix it. Use textContent for text. For HTML you trust, clean it first with DOMPurify.sanitize().
element.innerHTML = comment.body;element.textContent = comment.body;dangerouslySetInnerHTML without sanitizing
High
Why it matters. React renders the string as HTML, so any script or event handler in it runs (XSS).
How to fix it. Render text normally, or wrap the HTML with DOMPurify.sanitize() before passing it.
<div dangerouslySetInnerHTML={{ __html: post.body }} /><div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(post.body) }} />Unsafe settings
Security switched off: certificate checks, permissions, weak crypto, debug mode.
Certificate checks switched off
High
Why it matters. Without certificate checks anyone on the network can pretend to be the server and read or change the traffic.
How to fix it. Leave verification on. For a private certificate authority, point to its certificate (verify="ca.pem", ca: fs.readFileSync(...)).
requests.get(url, verify=False)requests.get(url, verify="/etc/ssl/company-ca.pem")Every certificate or host name accepted
High
Why it matters. A verifier that always says yes turns off HTTPS protection: anyone on the network can intercept the connection.
How to fix it. Use the platform's default verification. For a private CA, add it to a trust store instead.
conn.setHostnameVerifier((host, session) -> true);// keep the default HostnameVerifier
conn.connect();Permissions 777
High
Why it matters. 777 lets every user on the machine read, change and run the file, including another service if one is compromised.
How to fix it. Give only what's needed: 755 for programs and folders, 644 for files, 600 for secrets.
chmod -R 777 /var/www/uploadschmod -R 755 /var/www/uploadsMD5 or SHA-1 for passwords
High
Why it matters. MD5 and SHA-1 are fast, so a leaked password table is cracked in hours.
How to fix it. Use a password hash: bcrypt, scrypt or Argon2 (argon2-cffi, bcrypt, password_hash() in PHP).
hashed = hashlib.md5(password.encode()).hexdigest()hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt())Predictable random numbers for a secret
Medium
Why it matters. Math.random, random.random and rand() can be predicted, so tokens, passwords or codes made with them can be guessed.
How to fix it. Use a cryptographic source: crypto.randomUUID() / crypto.getRandomValues, secrets.token_urlsafe(), crypto/rand, SecureRandom, random_bytes().
const token = Math.random().toString(36).slice(2);const token = crypto.randomUUID();ECB encryption mode
High
Why it matters. ECB encrypts equal blocks to equal output, so patterns in the data stay visible and blocks can be swapped.
How to fix it. Use an authenticated mode: AES-GCM (or ChaCha20-Poly1305) with a new random nonce each time.
cipher = AES.new(key, AES.MODE_ECB)cipher = AES.new(key, AES.MODE_GCM)JWT signature not checked
Critical
Why it matters. Without the signature check anyone can write their own token and sign in as any user.
How to fix it. Verify with the expected key and algorithm: jwt.decode(token, key, algorithms=["HS256"]), jwt.verify(token, key).
claims = jwt.decode(token, options={"verify_signature": False})claims = jwt.decode(token, key, algorithms=["HS256"])Flask debug mode on
High
Why it matters. The debugger page lets anyone who can reach the app run Python on the server.
How to fix it. Turn debug on only through an environment variable in development, never in code that ships.
app.run(host="0.0.0.0", debug=True)app.run(host="0.0.0.0", debug=os.environ.get("FLASK_DEBUG") == "1")CORS open to every site
Medium
Why it matters. Allowing every origin lets any website call this API from a visitor's browser.
How to fix it. List the sites that may call it: cors({ origin: ["https://app.example.com"] }).
app.use(cors({ origin: "*" }));app.use(cors({ origin: ["https://app.example.com"] }));CSRF protection switched off
Medium
Why it matters. Without it another site can make a signed-in visitor's browser send requests here (cross-site request forgery).
How to fix it. Keep CSRF protection on for routes that use cookies. Turn it off only for token-authenticated APIs.
http.csrf().disable();http.csrf(Customizer.withDefaults());Downloaded script piped into a shell
Medium
Why it matters. Whatever the server sends runs right away, with no chance to check it; a hijacked or mistyped URL runs its code.
How to fix it. Download to a file, check its checksum or signature, then run it.
curl -fsSL https://example.com/install.sh | sudo bashcurl -fsSLo install.sh https://example.com/install.sh
sha256sum -c install.sh.sha256
bash install.sh