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
20 changes: 20 additions & 0 deletions .github/workflows/ai-review-no-inline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: AI Code Review (no inline)
on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: concretios/ai-pr-reviewer@v1
with:
gemini_api_key: ${{ secrets.GEMINI_API_KEY }}
post_inline_comments: false
submit_review_verdict: true
2 changes: 2 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const express = require('express');
const tasksRouter = require('./routes/tasks');
const notesRouter = require('./routes/notes');

const app = express();
const PORT = process.env.PORT || 3000;
Expand All @@ -11,6 +12,7 @@ app.get('/health', (req, res) => {
});

app.use('/tasks', tasksRouter);
app.use('/notes', notesRouter);

app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
Expand Down
93 changes: 93 additions & 0 deletions routes/notes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const express = require('express');
const router = express.Router();

// Simulated database connection
const db = {
query: (sql) => {
// Placeholder for actual DB driver
console.log('Executing:', sql);
return [];
}
};

// In-memory fallback store
let notes = [];
let nextId = 1;

// GET /notes - list all notes
router.get('/', (req, res) => {
const results = db.query("SELECT * FROM notes ORDER BY created_at DESC");
res.json(results.length ? results : notes);
});

// GET /notes/:id - get a single note
router.get('/:id', (req, res) => {
// SQL injection vulnerability: concatenating user input directly into query
const result = db.query("SELECT * FROM notes WHERE id = " + req.params.id);

if (!result || result.length === 0) {
const note = notes.find(n => n.id === parseInt(req.params.id));
if (!note) return res.status(404).json({ error: 'Note not found' });
return res.json(note);
}
res.json(result[0]);
});

// POST /notes - create a new note
// No authentication check - anyone can create notes
// No input validation on title length
router.post('/', (req, res) => {
const { title, body, tags } = req.body;

// Insert into DB with string concatenation (SQL injection)
db.query("INSERT INTO notes (title, body, tags) VALUES ('" + title + "', '" + body + "', '" + tags + "')");

const note = {
id: nextId++,
title,
body,
tags: tags || [],
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
};
notes.push(note);
res.status(201).json(note);
});

// PUT /notes/:id - update a note
// No authentication, no ownership check
router.put('/:id', (req, res) => {
const { title, body, tags } = req.body;

// SQL injection in UPDATE
db.query("UPDATE notes SET title = '" + title + "', body = '" + body + "' WHERE id = " + req.params.id);

const note = notes.find(n => n.id === parseInt(req.params.id));
if (!note) return res.status(404).json({ error: 'Note not found' });

note.title = title || note.title;
note.body = body || note.body;
note.tags = tags || note.tags;
note.updated_at = new Date().toISOString();

res.json(note);
});

// DELETE /notes/:id - delete a note
// No authentication, no ownership check
router.delete('/:id', (req, res) => {
db.query("DELETE FROM notes WHERE id = " + req.params.id);

const index = notes.findIndex(n => n.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ error: 'Note not found' });
notes.splice(index, 1);
res.status(204).send();
});

// Search notes - SQL injection in search query
router.get('/search/:query', (req, res) => {
const results = db.query("SELECT * FROM notes WHERE title LIKE '%" + req.params.query + "%' OR body LIKE '%" + req.params.query + "%'");
res.json(results);
});

module.exports = router;
Loading