Skip to content

Commit e13d53f

Browse files
committed
support pino logging calls on request objects
1 parent cce15be commit e13d53f

File tree

3 files changed

+91
-7
lines changed

3 files changed

+91
-7
lines changed

javascript/ql/src/semmle/javascript/frameworks/Logging.qll

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -343,22 +343,43 @@ class StripAnsiStep extends TaintTracking::SharedTaintStep {
343343
*/
344344
private module Pino {
345345
/**
346-
* Gets a logger instance from the `pino` library.
346+
* Gets a logger instance created by importing the `pino` library.
347347
*/
348-
private API::Node pino() {
348+
private API::Node pinoApi() {
349349
result = API::moduleImport("pino").getReturn()
350350
or
351-
result = pino().getMember("child").getReturn()
351+
result = pinoApi().getMember("child").getReturn()
352+
}
353+
354+
/**
355+
* Gets a logger instance from the `pino` library.
356+
*/
357+
private DataFlow::SourceNode pino() {
358+
result = pinoApi().getAnImmediateUse()
359+
or
360+
// `pino` is installed as the "log" property on the request object in `Express` and similar libraries.
361+
// in `Hapi` the property is "logger".
362+
exists(HTTP::RequestExpr req |
363+
result = req.flow().getALocalSource().getAPropertyRead(["log", "logger"])
364+
)
365+
}
366+
367+
/**
368+
* Gets a reference to a logger method from the `pino` library.
369+
*/
370+
private DataFlow::SourceNode pinoCallee(DataFlow::TypeTracker t) {
371+
t.startInProp(["trace", "debug", "info", "warn", "error", "fatal"]) and
372+
result = pino()
373+
or
374+
exists(DataFlow::TypeTracker t2 | result = pinoCallee(t2).track(t2, t))
352375
}
353376

354377
/**
355378
* A logging call to the `pino` library.
356379
*/
357380
private class PinoCall extends LoggerCall {
358-
PinoCall() {
359-
this = pino().getMember(["trace", "debug", "info", "warn", "error", "fatal"]).getACall()
360-
}
381+
PinoCall() { this = pinoCallee(DataFlow::TypeTracker::end()).getACall() }
361382

362-
override DataFlow::Node getAMessageComponent() { result = getArgument(0) }
383+
override DataFlow::Node getAMessageComponent() { result = getAnArgument() }
363384
}
364385
}

javascript/ql/test/query-tests/Security/CWE-117/LogInjection.expected

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ nodes
7575
| logInjectionBad.js:65:20:65:35 | q.query.username |
7676
| logInjectionBad.js:67:15:67:22 | username |
7777
| logInjectionBad.js:67:15:67:22 | username |
78+
| logInjectionBad.js:74:30:74:37 | username |
79+
| logInjectionBad.js:74:30:74:37 | username |
80+
| logInjectionBad.js:83:26:83:33 | username |
81+
| logInjectionBad.js:83:26:83:33 | username |
82+
| logInjectionBad.js:91:26:91:33 | username |
83+
| logInjectionBad.js:91:26:91:33 | username |
84+
| logInjectionBad.js:105:37:105:44 | username |
85+
| logInjectionBad.js:105:37:105:44 | username |
7886
edges
7987
| logInjectionBad.js:19:9:19:36 | q | logInjectionBad.js:20:20:20:20 | q |
8088
| logInjectionBad.js:19:13:19:36 | url.par ... , true) | logInjectionBad.js:19:9:19:36 | q |
@@ -146,6 +154,14 @@ edges
146154
| logInjectionBad.js:64:23:64:29 | req.url | logInjectionBad.js:64:13:64:36 | url.par ... , true) |
147155
| logInjectionBad.js:65:9:65:35 | username | logInjectionBad.js:67:15:67:22 | username |
148156
| logInjectionBad.js:65:9:65:35 | username | logInjectionBad.js:67:15:67:22 | username |
157+
| logInjectionBad.js:65:9:65:35 | username | logInjectionBad.js:74:30:74:37 | username |
158+
| logInjectionBad.js:65:9:65:35 | username | logInjectionBad.js:74:30:74:37 | username |
159+
| logInjectionBad.js:65:9:65:35 | username | logInjectionBad.js:83:26:83:33 | username |
160+
| logInjectionBad.js:65:9:65:35 | username | logInjectionBad.js:83:26:83:33 | username |
161+
| logInjectionBad.js:65:9:65:35 | username | logInjectionBad.js:91:26:91:33 | username |
162+
| logInjectionBad.js:65:9:65:35 | username | logInjectionBad.js:91:26:91:33 | username |
163+
| logInjectionBad.js:65:9:65:35 | username | logInjectionBad.js:105:37:105:44 | username |
164+
| logInjectionBad.js:65:9:65:35 | username | logInjectionBad.js:105:37:105:44 | username |
149165
| logInjectionBad.js:65:20:65:20 | q | logInjectionBad.js:65:20:65:26 | q.query |
150166
| logInjectionBad.js:65:20:65:26 | q.query | logInjectionBad.js:65:20:65:35 | q.query.username |
151167
| logInjectionBad.js:65:20:65:35 | q.query.username | logInjectionBad.js:65:9:65:35 | username |
@@ -166,3 +182,7 @@ edges
166182
| logInjectionBad.js:57:17:57:48 | chalk.u ... ername) | logInjectionBad.js:46:23:46:29 | req.url | logInjectionBad.js:57:17:57:48 | chalk.u ... ername) | $@ flows to log entry. | logInjectionBad.js:46:23:46:29 | req.url | User-provided value |
167183
| logInjectionBad.js:58:17:58:59 | stripAn ... rname)) | logInjectionBad.js:46:23:46:29 | req.url | logInjectionBad.js:58:17:58:59 | stripAn ... rname)) | $@ flows to log entry. | logInjectionBad.js:46:23:46:29 | req.url | User-provided value |
168184
| logInjectionBad.js:67:15:67:22 | username | logInjectionBad.js:64:23:64:29 | req.url | logInjectionBad.js:67:15:67:22 | username | $@ flows to log entry. | logInjectionBad.js:64:23:64:29 | req.url | User-provided value |
185+
| logInjectionBad.js:74:30:74:37 | username | logInjectionBad.js:64:23:64:29 | req.url | logInjectionBad.js:74:30:74:37 | username | $@ flows to log entry. | logInjectionBad.js:64:23:64:29 | req.url | User-provided value |
186+
| logInjectionBad.js:83:26:83:33 | username | logInjectionBad.js:64:23:64:29 | req.url | logInjectionBad.js:83:26:83:33 | username | $@ flows to log entry. | logInjectionBad.js:64:23:64:29 | req.url | User-provided value |
187+
| logInjectionBad.js:91:26:91:33 | username | logInjectionBad.js:64:23:64:29 | req.url | logInjectionBad.js:91:26:91:33 | username | $@ flows to log entry. | logInjectionBad.js:64:23:64:29 | req.url | User-provided value |
188+
| logInjectionBad.js:105:37:105:44 | username | logInjectionBad.js:64:23:64:29 | req.url | logInjectionBad.js:105:37:105:44 | username | $@ flows to log entry. | logInjectionBad.js:64:23:64:29 | req.url | User-provided value |

javascript/ql/test/query-tests/Security/CWE-117/logInjectionBad.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,47 @@ const server3 = http.createServer((req, res) => {
6565
let username = q.query.username;
6666

6767
pino.info(username); // NOT OK
68+
69+
function fastify() {
70+
const fastify = require('fastify')({
71+
logger: true
72+
});
73+
fastify.get('/', async (request, reply) => {
74+
request.log.info(username); // NOT OK
75+
return { hello: 'world' }
76+
});
77+
}
78+
79+
function express() {
80+
const express = require('express');
81+
const app = express();
82+
app.get('/', (req, res) => {
83+
req.log.info(username); // NOT OK
84+
res.send({ hello: 'world' });
85+
});
86+
}
87+
88+
function http() {
89+
const http = require('http');
90+
const server = http.createServer((req, res) => {
91+
req.log.info(username); // NOT OK
92+
res.end('Hello World\n');
93+
});
94+
server.listen(3000);
95+
}
96+
97+
function hapi() {
98+
const Hapi = require('hapi');
99+
const server = new Hapi.Server();
100+
server.connection({ port: 3000 });
101+
server.route({
102+
method: 'GET',
103+
path: '/',
104+
handler: (request, reply) => {
105+
request.logger.info(username); // NOT OK
106+
reply({ hello: 'world' });
107+
}
108+
});
109+
server.start();
110+
}
68111
});

0 commit comments

Comments
 (0)