Skip to content
Merged
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 CWE-117/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const express = require('express')

const app = express()

app.get('/login', (req, res) => {
console.log(`Failed login attempt for user: ${req.query.user}`)
res.status(401).end()
})

app.listen(3000)
15 changes: 15 additions & 0 deletions CWE-1321/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const merge = (target, source) => {
for (const key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
if (!target[key]) target[key] = {}
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
return target
}

const main = (json) => {
return merge({}, JSON.parse(json))
}
3 changes: 3 additions & 0 deletions CWE-1333/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const main = (email) => {
return /^([a-zA-Z0-9]+)*@example\.com$/.test(email)
}
13 changes: 13 additions & 0 deletions CWE-209/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const express = require('express')

const app = express()

app.get('/settings', (req, res) => {
try {
res.json(JSON.parse(req.query.data))
} catch (err) {
res.status(500).send(err.stack)
}
})

app.listen(3000)
11 changes: 11 additions & 0 deletions CWE-22/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const express = require('express')
const path = require('path')

const app = express()

app.get('/download', (req, res) => {
const filePath = path.join(__dirname, 'uploads', req.query.file)
res.sendFile(filePath)
})
Comment on lines +6 to +9

app.listen(3000)
6 changes: 6 additions & 0 deletions CWE-327/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const crypto = require('crypto')

const main = (creditCardNumber, key) => {
const cipher = crypto.createCipheriv('des-ecb', key, null)
return cipher.update(creditCardNumber, 'utf8', 'hex') + cipher.final('hex')
}
17 changes: 17 additions & 0 deletions CWE-352/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const express = require('express')
const cookieParser = require('cookie-parser')

const app = express()
app.use(cookieParser())
app.use(express.urlencoded({ extended: false }))

const updateEmail = (sessionId, email) => {
console.log(`Session ${sessionId} changed email to ${email}`)
}

app.post('/account/email', (req, res) => {
updateEmail(req.cookies.sessionId, req.body.email)
res.redirect('/account')
})

app.listen(3000)
14 changes: 14 additions & 0 deletions CWE-502/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const express = require('express')
const cookieParser = require('cookie-parser')
const serialize = require('node-serialize')

const app = express()
app.use(cookieParser())

app.get('/profile', (req, res) => {
const data = Buffer.from(req.cookies.profile, 'base64').toString()
const profile = serialize.unserialize(data)
res.json({ name: profile.name })
})

app.listen(3000)
9 changes: 9 additions & 0 deletions CWE-601/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const express = require('express')

const app = express()

app.get('/login', (req, res) => {
res.redirect(req.query.returnUrl)
})

app.listen(3000)
12 changes: 12 additions & 0 deletions CWE-611/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const express = require('express')
const libxmljs = require('libxmljs')

const app = express()
app.use(express.text({ type: 'application/xml' }))

app.post('/import', (req, res) => {
const doc = libxmljs.parseXml(req.body, { noent: true })
res.json({ name: doc.get('//name').text() })
})

app.listen(3000)
12 changes: 12 additions & 0 deletions CWE-614/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const express = require('express')
const crypto = require('crypto')

const app = express()

app.post('/login', (req, res) => {
const sessionId = crypto.randomBytes(32).toString('hex')
res.cookie('sessionId', sessionId, { httpOnly: true, secure: false })
res.redirect('/')
})

app.listen(3000)
12 changes: 12 additions & 0 deletions CWE-78/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const express = require('express')
const { exec } = require('child_process')

const app = express()

app.get('/ping', (req, res) => {
exec(`ping -c 1 ${req.query.host}`, (err, stdout) => {
res.json({ output: stdout })
})
})
Comment on lines +6 to +10

app.listen(3000)
4 changes: 4 additions & 0 deletions CWE-79/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const main = () => {
const name = new URLSearchParams(window.location.search).get('name')
document.getElementById('greeting').innerHTML = `Hello ${name}`
}
16 changes: 16 additions & 0 deletions CWE-89/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const express = require('express')
const mysql = require('mysql')

const app = express()
const connection = mysql.createConnection({ host: 'localhost', database: 'shop' })

app.get('/products', (req, res) => {
const query = `SELECT * FROM products WHERE category = '${req.query.category}'`

connection.query(query, (err, rows) => {
if (err) return res.status(500).end()
res.json(rows)
})
})
Comment on lines +7 to +14

app.listen(3000)
5 changes: 5 additions & 0 deletions CWE-916/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const crypto = require('crypto')

const main = (password) => {
return crypto.createHash('md5').update(password).digest('hex')
}
11 changes: 11 additions & 0 deletions CWE-918/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const express = require('express')
const axios = require('axios')

const app = express()

app.get('/preview', async (req, res) => {
const response = await axios.get(req.query.url)
res.json({ status: response.status, length: response.data.length })
})

app.listen(3000)
15 changes: 15 additions & 0 deletions CWE-942/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const express = require('express')

const app = express()

app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
res.setHeader('Access-Control-Allow-Credentials', 'true')
next()
})

app.get('/api/profile', (req, res) => {
res.json({ user: 'admin', email: 'admin@example.com' })
})

app.listen(3000)
19 changes: 19 additions & 0 deletions CWE-943/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const express = require('express')
const { MongoClient } = require('mongodb')

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

const client = new MongoClient('mongodb://localhost:27017')
const users = client.db('shop').collection('users')

app.post('/login', async (req, res) => {
const user = await users.findOne({
username: req.body.username,
password: req.body.password,
})

res.json({ authenticated: Boolean(user) })
})
Comment on lines +10 to +17

app.listen(3000)
4 changes: 4 additions & 0 deletions CWE-95/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const main = () => {
const expression = decodeURIComponent(window.location.hash.slice(1))
return eval(expression)
}
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,43 @@
# JavaScript CWE CodeQL

Intentionally vulnerable JavaScript Code Snippets which are intended to benchmark Static Application Security Testing tools.

**Do not use any of this code in production.**

## Snippets

Each folder contains one `main.js` with exactly one intended weakness. Client-side snippets run in the browser, server-side snippets are minimal [Express](https://expressjs.com/) apps. Dependencies are only referenced via `require` and are not installed, which is sufficient for static analysis.

| CWE | Name | Context | Weakness |
| --- | --- | --- | --- |
| [CWE-20](CWE-20/main.js) | Improper Input Validation | Client | Suffix check returns `true` if the suffix is one character longer than the input |
| [CWE-22](CWE-22/main.js) | Path Traversal | Server | `req.query.file` is joined into a file path, `../` escapes the upload folder |
| [CWE-78](CWE-78/main.js) | OS Command Injection | Server | `req.query.host` is interpolated into a shell command |
| [CWE-79](CWE-79/main.js) | Cross-site Scripting (DOM) | Client | URL parameter is written to `innerHTML` |
| [CWE-80](CWE-80/main.js) | Basic XSS | Client | HTML is only inserted if it contains a `<script>` tag |
| [CWE-89](CWE-89/main.js) | SQL Injection | Server | `req.query.category` is concatenated into an SQL query |
| [CWE-95](CWE-95/main.js) | Eval Injection | Client | URL fragment is passed to `eval` |
| [CWE-117](CWE-117/main.js) | Log Injection | Server | Unsanitized user name is logged, newlines forge log entries |
| [CWE-209](CWE-209/main.js) | Information Exposure Through an Error Message | Server | Stack trace is sent to the client |
| [CWE-256](CWE-256/main.js) | Plaintext Storage of a Password | Client | Password is stored in `localStorage` |
| [CWE-258](CWE-258/main.js) | Empty Password in Configuration File | Server | Admin login succeeds with an empty password |
| [CWE-259](CWE-259/main.js) | Use of Hard-coded Password | Client | Bearer token is hard-coded |
| [CWE-312](CWE-312/main.js) | Cleartext Storage of Sensitive Information | Client | Credit card number is stored in a cookie |
| [CWE-327](CWE-327/main.js) | Use of a Broken Cryptographic Algorithm | Server | Credit card number is encrypted with DES-ECB |
| [CWE-330](CWE-330/main.js) | Use of Insufficiently Random Values | Client | Reset token is generated with `Math.random()` |
| [CWE-338](CWE-338/main.js) | Use of Cryptographically Weak PRNG | Client | Password is generated with `Math.random()` |
| [CWE-352](CWE-352/main.js) | Cross-Site Request Forgery | Server | State-changing POST request relies on cookies without a CSRF token |
| [CWE-502](CWE-502/main.js) | Deserialization of Untrusted Data | Server | Cookie is deserialized with `node-serialize` |
| [CWE-561](CWE-561/main.js) | Dead Code | Client | Unreachable branch skips the ban check |
| [CWE-563](CWE-563/main.js) | Assignment to Variable without Use | Client | Sanitized input is unused, raw input goes to `innerHTML` |
| [CWE-601](CWE-601/main.js) | Open Redirect | Server | `req.query.returnUrl` is used as redirect target |
| [CWE-611](CWE-611/main.js) | XML External Entity | Server | XML is parsed with entity expansion enabled |
| [CWE-614](CWE-614/main.js) | Sensitive Cookie Without 'Secure' Attribute | Server | Session cookie is sent over HTTP |
| [CWE-834](CWE-834/main.js) | Excessive Iteration | Client | Loop bound is taken from the API response |
| [CWE-916](CWE-916/main.js) | Weak Password Hash | Server | Password is hashed with MD5 |
| [CWE-918](CWE-918/main.js) | Server-Side Request Forgery | Server | Server fetches `req.query.url` |
| [CWE-942](CWE-942/main.js) | Permissive CORS Policy | Server | Any origin is reflected with credentials allowed |
| [CWE-943](CWE-943/main.js) | NoSQL Injection | Server | Request body is used as MongoDB query, `{"$ne": null}` bypasses login |
| [CWE-1022](CWE-1022/main.js) | Untrusted Link Target | Client | `window.open` without `noopener` |
| [CWE-1321](CWE-1321/main.js) | Prototype Pollution | Client | Recursive merge copies `__proto__` from parsed JSON |
| [CWE-1333](CWE-1333/main.js) | Inefficient Regular Expression (ReDoS) | Client | Nested quantifier causes catastrophic backtracking |
Loading