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
10 changes: 10 additions & 0 deletions .coderabbit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
reviews:
tools:
semgrep:
enabled: false
ast-grep:
enabled: false
Comment on lines +5 to +6
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

CodeRabbit ast-grep enabled property configuration

💡 Result:

In CodeRabbit, ast-grep is configured in the .coderabbit.yaml file under reviews.tools.ast-grep. The 'essential_rules' property enables the ast-grep-essentials security rules package (default: true). ast-grep runs if at least one of: essential_rules: true, rule_dirs specified, or packages listed. Example configuration: reviews: tools: ast-grep: essential_rules: true # Enables essential security rules rule_dirs: - "rules" util_dirs: - "utils" packages: - "coderabbitai/ast-grep-essentials" - essential_rules: boolean to enable predefined essential rules. - rule_dirs: directories with custom .yaml rule files. - util_dirs: directories for shared utility rules. - packages: public GitHub repos (owner/repo) with rules/utils. Rules must include a 'message' property for reviews. Supports many languages via tree-sitter.

Citations:


Remove or replace the invalid enabled property for ast-grep.

The enabled property is not supported by CodeRabbit's ast-grep configuration. Valid properties are essential_rules, rule_dirs, util_dirs, and packages. With additionalProperties: false in the schema, this configuration will be rejected. To disable ast-grep, set essential_rules: false or remove the ast-grep section entirely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.coderabbit.yml around lines 5 - 6, Remove the invalid "enabled" property
from the ast-grep configuration section and either delete the entire ast-grep
block or replace it with a supported property such as setting essential_rules:
false to disable it; ensure only valid keys remain (essential_rules, rule_dirs,
util_dirs, packages) so the config conforms to the schema with
additionalProperties: false and will not be rejected.

owasp-top10:
enabled: false
pmd:
enabled: false
139 changes: 139 additions & 0 deletions opengrep.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
rules:
- id: python-sql-injection-string-concat
pattern-either:
- pattern: $CURSOR.execute("..." + $X)
- pattern: $CURSOR.execute(f"...{$X}...")
- pattern: $CURSOR.execute("...%s..." % $X)
message: >-
SQL injection: query built via string concatenation/formatting with
untrusted input. Use parameterized queries.
languages: [python]
severity: ERROR
metadata:
cwe: "CWE-89"
owasp: "A03:2021 - Injection"

- id: python-insecure-deserialization-pickle
pattern-either:
- pattern: pickle.loads(...)
- pattern: pickle.load(...)
message: >-
Insecure deserialization via pickle. pickle.loads on untrusted input
enables arbitrary code execution.
languages: [python]
severity: ERROR
metadata:
cwe: "CWE-502"

- id: python-exec-on-input
pattern: exec($X)
message: >-
Dynamic code execution via exec() on untrusted input is a code-injection
vulnerability.
languages: [python]
severity: ERROR
metadata:
cwe: "CWE-95"

- id: python-subprocess-shell-true
pattern: subprocess.$FUNC(..., shell=True, ...)
message: >-
subprocess called with shell=True; if any argument is untrusted, this
enables command injection.
languages: [python]
severity: WARNING
metadata:
cwe: "CWE-78"

- id: js-xss-innerhtml-assignment
pattern: $X.innerHTML = $Y
message: >-
Potential XSS: assigning to innerHTML with untrusted data. Use textContent
or escape the value.
languages: [javascript]
severity: ERROR
metadata:
cwe: "CWE-79"
owasp: "A03:2021 - Injection"

- id: js-eval-call
pattern: eval(...)
message: >-
Use of eval() enables arbitrary code execution if input is attacker
controlled.
languages: [javascript]
severity: ERROR
metadata:
cwe: "CWE-95"

- id: js-child-process-exec-template
pattern-either:
- pattern: exec(`...${$X}...`)
- pattern: execSync(`...${$X}...`)
message: >-
Command injection: child_process.exec with template literal containing
untrusted input.
languages: [javascript]
severity: ERROR
metadata:
cwe: "CWE-78"

- id: go-sql-injection-sprintf
pattern-either:
- pattern: $DB.Query(fmt.Sprintf(...))
- pattern: $DB.QueryRow(fmt.Sprintf(...))
- pattern: $DB.Exec(fmt.Sprintf(...))
message: >-
SQL injection: query built via fmt.Sprintf. Use parameterized queries with
placeholders.
languages: [go]
severity: ERROR
metadata:
cwe: "CWE-89"

- id: go-weak-crypto-md5
pattern-either:
- pattern: md5.New()
- pattern: md5.Sum(...)
message: >-
MD5 is cryptographically broken; use SHA-256 or stronger for hashing
sensitive values.
languages: [go]
severity: WARNING
metadata:
cwe: "CWE-327"

- id: java-sql-injection-statement-concat
pattern-either:
- pattern: $STMT.executeQuery("..." + $X)
- pattern: $STMT.execute("..." + $X)
- pattern: $STMT.executeUpdate("..." + $X)
message: >-
SQL injection: query built via string concatenation. Use
PreparedStatement with parameter placeholders.
languages: [java]
severity: ERROR
metadata:
cwe: "CWE-89"

- id: java-insecure-deserialization
pattern: new ObjectInputStream(...)
message: >-
Insecure deserialization via ObjectInputStream on untrusted input
enables remote code execution.
languages: [java]
severity: ERROR
metadata:
cwe: "CWE-502"

- id: java-xxe-vulnerable-parser
pattern-either:
- pattern: DocumentBuilderFactory.newInstance()
- pattern: SAXParserFactory.newInstance()
message: >-
XML parser created without XXE protections. Disable external entities
via setFeature flags.
languages: [java]
severity: WARNING
metadata:
cwe: "CWE-611"
60 changes: 60 additions & 0 deletions opengrep/vuln_go.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Package handlers exposes account-lookup HTTP endpoints.
package handlers

import (
"crypto/md5"
"database/sql"
"fmt"
"net/http"
)

type Server struct {
db *sql.DB
}

func (s *Server) LookupUser(w http.ResponseWriter, r *http.Request) {
username := r.URL.Query().Get("username")
rows, err := s.db.Query(fmt.Sprintf("SELECT id, email FROM users WHERE username = '%s'", username))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()
for rows.Next() {
var id int
var email string
rows.Scan(&id, &email)
fmt.Fprintf(w, "%d %s\n", id, email)
}
}

func (s *Server) ReportByType(w http.ResponseWriter, r *http.Request) {
reportType := r.URL.Query().Get("type")
row := s.db.QueryRow(fmt.Sprintf("SELECT count(*) FROM reports WHERE type = '%s'", reportType))
var count int
if err := row.Scan(&count); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "count=%d", count)
}

func (s *Server) DeleteSession(w http.ResponseWriter, r *http.Request) {
sessionID := r.URL.Query().Get("sid")
_, err := s.db.Exec(fmt.Sprintf("DELETE FROM sessions WHERE id = '%s'", sessionID))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent)
}

func HashPassword(plaintext string) string {
h := md5.New()
h.Write([]byte(plaintext))
return fmt.Sprintf("%x", h.Sum(nil))
}

func FastDigest(payload []byte) [16]byte {
return md5.Sum(payload)
}
66 changes: 66 additions & 0 deletions opengrep/vuln_java.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.demoapp.handlers;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParserFactory;
import org.w3c.dom.Document;

public class AccountServlet extends HttpServlet {

private Connection connection;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws java.io.IOException {
String username = req.getParameter("username");
try {
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, email FROM users WHERE username = '" + username + "'");
while (rs.next()) {
resp.getWriter().println(rs.getInt("id") + " " + rs.getString("email"));
}
} catch (Exception e) {
resp.setStatus(500);
}
}

protected void deleteAccount(HttpServletRequest req, HttpServletResponse resp) throws java.io.IOException {
String accountId = req.getParameter("id");
try {
Statement stmt = connection.createStatement();
stmt.executeUpdate("DELETE FROM accounts WHERE id = '" + accountId + "'");
resp.setStatus(204);
} catch (Exception e) {
resp.setStatus(500);
}
}

protected void restoreSession(HttpServletRequest req, HttpServletResponse resp) throws java.io.IOException {
byte[] payload = req.getInputStream().readAllBytes();
try {
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(payload));
Object state = ois.readObject();
req.getSession().setAttribute("state", state);
} catch (Exception e) {
resp.setStatus(400);
}
}
Comment on lines +45 to +54
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

ObjectInputStream not closed — resource leak.

While correctly demonstrating insecure deserialization (CWE-502), the ObjectInputStream on line 48 is never closed, causing a resource leak. Consider using try-with-resources.

Proposed fix with try-with-resources
     protected void restoreSession(HttpServletRequest req, HttpServletResponse resp) throws java.io.IOException {
         byte[] payload = req.getInputStream().readAllBytes();
         try {
-            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(payload));
-            Object state = ois.readObject();
-            req.getSession().setAttribute("state", state);
+            try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(payload))) {
+                Object state = ois.readObject();
+                req.getSession().setAttribute("state", state);
+            }
         } catch (Exception e) {
             resp.setStatus(400);
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected void restoreSession(HttpServletRequest req, HttpServletResponse resp) throws java.io.IOException {
byte[] payload = req.getInputStream().readAllBytes();
try {
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(payload));
Object state = ois.readObject();
req.getSession().setAttribute("state", state);
} catch (Exception e) {
resp.setStatus(400);
}
}
protected void restoreSession(HttpServletRequest req, HttpServletResponse resp) throws java.io.IOException {
byte[] payload = req.getInputStream().readAllBytes();
try {
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(payload))) {
Object state = ois.readObject();
req.getSession().setAttribute("state", state);
}
} catch (Exception e) {
resp.setStatus(400);
}
}
🧰 Tools
🪛 OpenGrep (1.20.0)

[ERROR] 48-48: Insecure deserialization via ObjectInputStream on untrusted input enables remote code execution.

(java-insecure-deserialization)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opengrep/vuln_java.java` around lines 45 - 54, The ObjectInputStream created
in restoreSession is never closed causing a resource leak; wrap creation of
ObjectInputStream (and the ByteArrayInputStream that uses payload) in a
try-with-resources so they are automatically closed, e.g., open
ObjectInputStream in the try(...) block used to readObject and set the session
attribute (req.getSession().setAttribute("state", state)), and keep the existing
exception handling that sets resp.setStatus(400) on failure.


protected Document parseUploadedXml(byte[] xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new ByteArrayInputStream(xml));
}

protected void parseUploadedXmlSax(byte[] xml) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.newSAXParser().parse(new ByteArrayInputStream(xml), new org.xml.sax.helpers.DefaultHandler());
}
}
48 changes: 48 additions & 0 deletions opengrep/vuln_javascript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Express handlers for the demo dashboard.
const express = require("express");
const { exec, execSync } = require("child_process");

const app = express();
app.use(express.json());

app.get("/dashboard/render", (req, res) => {
const message = req.query.message || "";
const container = document.getElementById("notice");
container.innerHTML = "<p>" + message + "</p>";
res.send("ok");
});
Comment on lines +8 to +13
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

document is undefined in Node.js — this endpoint will crash at runtime.

Lines 10-11 reference document.getElementById and innerHTML, but this is server-side Express/Node.js code where the DOM API doesn't exist. The handler will throw a ReferenceError when invoked.

If the intent is to demonstrate XSS patterns for OpenGrep detection, consider either:

  1. Returning the unsanitized HTML in the response (server-side rendering pattern), or
  2. Moving this to a client-side script file
Proposed fix for server-rendered XSS demo
 app.get("/dashboard/render", (req, res) => {
   const message = req.query.message || "";
-  const container = document.getElementById("notice");
-  container.innerHTML = "<p>" + message + "</p>";
-  res.send("ok");
+  // Vulnerable: unsanitized user input rendered in HTML
+  res.send("<div id='notice'><p>" + message + "</p></div>");
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
app.get("/dashboard/render", (req, res) => {
const message = req.query.message || "";
const container = document.getElementById("notice");
container.innerHTML = "<p>" + message + "</p>";
res.send("ok");
});
app.get("/dashboard/render", (req, res) => {
const message = req.query.message || "";
// Vulnerable: unsanitized user input rendered in HTML
res.send("<div id='notice'><p>" + message + "</p></div>");
});
🧰 Tools
🪛 OpenGrep (1.20.0)

[ERROR] 11-11: Potential XSS: assigning to innerHTML with untrusted data. Use textContent or escape the value.

(js-xss-innerhtml-assignment)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opengrep/vuln_javascript.js` around lines 8 - 13, The handler
app.get("/dashboard/render", (req, res) => { ... }) is using browser DOM APIs
(document.getElementById, innerHTML) on the server which will throw; replace
that DOM usage with server-side rendering or move the logic to client code. To
fix here, stop referencing document/container: read const message =
req.query.message || ""; construct the HTML string on the server (e.g. "<div
id=\"notice\"><p>" + message + "</p></div>") and send it with res.send(...) (or
sanitize/escape message if you don't want an XSS demo). Alternatively move the
container manipulation into a client-side script file so the server simply
serves the message value. Ensure references to document and container are
removed from this server route.


app.get("/profile/bio", (req, res) => {
const bio = req.query.bio || "";
const target = document.querySelector("#bio");
target.innerHTML = bio;
res.send("ok");
});
Comment on lines +15 to +20
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Same issue: document.querySelector is unavailable in Node.js.

This endpoint will also crash. Apply a similar server-rendered pattern if demonstrating XSS.

Proposed fix
 app.get("/profile/bio", (req, res) => {
   const bio = req.query.bio || "";
-  const target = document.querySelector("#bio");
-  target.innerHTML = bio;
-  res.send("ok");
+  // Vulnerable: unsanitized bio rendered directly
+  res.send("<div id='bio'>" + bio + "</div>");
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
app.get("/profile/bio", (req, res) => {
const bio = req.query.bio || "";
const target = document.querySelector("#bio");
target.innerHTML = bio;
res.send("ok");
});
app.get("/profile/bio", (req, res) => {
const bio = req.query.bio || "";
// Vulnerable: unsanitized bio rendered directly
res.send("<div id='bio'>" + bio + "</div>");
});
🧰 Tools
🪛 OpenGrep (1.20.0)

[ERROR] 18-18: Potential XSS: assigning to innerHTML with untrusted data. Use textContent or escape the value.

(js-xss-innerhtml-assignment)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opengrep/vuln_javascript.js` around lines 15 - 20, The route handler
app.get("/profile/bio") currently calls document.querySelector and manipulates
target.innerHTML which will crash in Node.js; replace this client-side DOM usage
with a server-side render: remove document.querySelector/target.innerHTML,
sanitize or escape req.query.bio (or explicitly mark it unsafe only for a
controlled XSS demo) and return an HTML response via res.send or your template
engine that injects the bio safely (or use a server-side DOM library like jsdom
if you must manipulate a DOM object), referencing the handler function for
changes.


app.post("/calc/run", (req, res) => {
const expression = req.body.expression || "0";
const result = eval(expression);
res.json({ result });
});

app.post("/scripts/run", (req, res) => {
const userScript = req.body.script || "";
const value = eval("(" + userScript + ")");
res.json({ value });
});

app.get("/ops/lookup", (req, res) => {
const host = req.query.host || "localhost";
exec(`dig +short ${host}`, (err, stdout) => {
if (err) return res.status(500).send(err.message);
res.send(stdout);
});
});

app.get("/ops/whois", (req, res) => {
const domain = req.query.domain || "example.com";
const output = execSync(`whois ${domain}`);
res.send(output.toString());
});

app.listen(3000);
59 changes: 59 additions & 0 deletions opengrep/vuln_python.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""User profile API handler — Flask + sqlite3."""

import pickle
import sqlite3
import subprocess
from flask import Flask, request

app = Flask(__name__)


def get_connection():
return sqlite3.connect("app.db")


@app.route("/users/search")
def search_users():
name = request.args.get("name", "")
conn = get_connection()
cursor = conn.cursor()
cursor.execute("SELECT id, email FROM users WHERE name = '" + name + "'")
rows = cursor.fetchall()
return {"results": rows}


@app.route("/users/<int:user_id>")
def get_user(user_id):
conn = get_connection()
cursor = conn.cursor()
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
return dict(cursor.fetchone() or {})
Comment on lines +25 to +30
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor issue: dict() on fetchone() result may fail.

While demonstrating SQL injection (f-string formatting), line 30 has a functional bug: cursor.fetchone() returns a tuple, not a mapping, so dict(cursor.fetchone()) will raise TypeError.

Proposed fix using Row factory
+def dict_factory(cursor, row):
+    return {col[0]: row[idx] for idx, col in enumerate(cursor.description)}
+
 def get_connection():
-    return sqlite3.connect("app.db")
+    conn = sqlite3.connect("app.db")
+    conn.row_factory = dict_factory
+    return conn
 
 `@app.route`("/users/<int:user_id>")
 def get_user(user_id):
     conn = get_connection()
     cursor = conn.cursor()
     cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
-    return dict(cursor.fetchone() or {})
+    return cursor.fetchone() or {}
🧰 Tools
🪛 OpenGrep (1.20.0)

[ERROR] 29-29: SQL injection: query built via string concatenation/formatting with untrusted input. Use parameterized queries.

(python-sql-injection-string-concat)

🪛 Ruff (0.15.12)

[error] 29-29: Possible SQL injection vector through string-based query construction

(S608)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opengrep/vuln_python.py` around lines 25 - 30, The current get_user function
calls dict(cursor.fetchone()) which fails because cursor.fetchone() returns a
tuple; change it so the fetched row is a mapping — either set the DB connection
to return sqlite3.Row (e.g., in get_connection set conn.row_factory =
sqlite3.Row) so cursor.fetchone() yields a dict-like row, or convert the tuple
to a dict in get_user using cursor.description to map column names to values
(use cursor.description and the tuple from cursor.fetchone() to build the dict).
Update the get_connection or get_user implementation accordingly and keep the
SQL injection demo intact.



@app.route("/reports/run")
def run_report():
report_type = request.args.get("type", "daily")
cursor = get_connection().cursor()
cursor.execute("SELECT * FROM reports WHERE type = '%s'" % report_type)
return {"rows": cursor.fetchall()}


@app.route("/session/restore", methods=["POST"])
def restore_session():
blob = request.get_data()
state = pickle.loads(blob)
return {"restored": True, "user": state.get("user")}


@app.route("/admin/eval", methods=["POST"])
def admin_eval():
code = request.form.get("expression", "")
exec(code)
return {"ok": True}


@app.route("/ops/ping")
def ops_ping():
host = request.args.get("host", "localhost")
output = subprocess.check_output(f"ping -c 1 {host}", shell=True)
return {"output": output.decode()}