diff --git a/.coderabbit.yml b/.coderabbit.yml new file mode 100644 index 0000000..5fa3438 --- /dev/null +++ b/.coderabbit.yml @@ -0,0 +1,10 @@ +reviews: + tools: + semgrep: + enabled: false + ast-grep: + enabled: false + owasp-top10: + enabled: false + pmd: + enabled: false diff --git a/opengrep.yml b/opengrep.yml new file mode 100644 index 0000000..765fc6f --- /dev/null +++ b/opengrep.yml @@ -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" diff --git a/opengrep/vuln_go.go b/opengrep/vuln_go.go new file mode 100644 index 0000000..8288a32 --- /dev/null +++ b/opengrep/vuln_go.go @@ -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) +} diff --git a/opengrep/vuln_java.java b/opengrep/vuln_java.java new file mode 100644 index 0000000..682ede3 --- /dev/null +++ b/opengrep/vuln_java.java @@ -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); + } + } + + 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()); + } +} diff --git a/opengrep/vuln_javascript.js b/opengrep/vuln_javascript.js new file mode 100644 index 0000000..f594e9f --- /dev/null +++ b/opengrep/vuln_javascript.js @@ -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 = "
" + message + "
"; + res.send("ok"); +}); + +app.get("/profile/bio", (req, res) => { + const bio = req.query.bio || ""; + const target = document.querySelector("#bio"); + target.innerHTML = bio; + res.send("ok"); +}); + +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); diff --git a/opengrep/vuln_python.py b/opengrep/vuln_python.py new file mode 100644 index 0000000..0a0edaa --- /dev/null +++ b/opengrep/vuln_python.py @@ -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/