mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
@@ -0,0 +1,3 @@
|
||||
import testUtilities.ConsistencyChecking
|
||||
import semmle.javascript.security.dataflow.ReflectedXss
|
||||
import semmle.javascript.security.dataflow.ServerSideUrlRedirect
|
||||
@@ -0,0 +1,10 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
app.useGlobalPipes(new ValidationPipe());
|
||||
await app.listen(3000);
|
||||
}
|
||||
bootstrap();
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Get, Query } from '@nestjs/common';
|
||||
import { IsIn } from 'class-validator';
|
||||
|
||||
export class Controller {
|
||||
@Get()
|
||||
route1(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) {
|
||||
if (Math.random()) return unvalidated; // NOT OK
|
||||
return validatedObj.key; // OK
|
||||
}
|
||||
}
|
||||
|
||||
class Struct {
|
||||
@IsIn(['foo', 'bar'])
|
||||
key: string;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Get, createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||
|
||||
export const SneakyQueryParam = createParamDecorator(
|
||||
(data: unknown, ctx: ExecutionContext) => {
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
return request.query.sneakyQueryParam;
|
||||
},
|
||||
);
|
||||
|
||||
export const SafeParam = createParamDecorator(
|
||||
(data: unknown, ctx: ExecutionContext) => {
|
||||
return 'Safe';
|
||||
},
|
||||
);
|
||||
|
||||
export class Controller {
|
||||
@Get()
|
||||
sneaky(@SneakyQueryParam() value) {
|
||||
return value; // NOT OK
|
||||
}
|
||||
|
||||
@Get()
|
||||
safe(@SafeParam() value) {
|
||||
return value; // OK
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Get, Injectable, PipeTransform, Query, UsePipes } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class CustomSanitizingPipe implements PipeTransform {
|
||||
transform(value: string): number | undefined {
|
||||
if (value == null) return undefined;
|
||||
return Number(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CustomPropagatingPipe implements PipeTransform {
|
||||
transform(value: string): string {
|
||||
return value.toUpperCase() + '!';
|
||||
}
|
||||
}
|
||||
|
||||
export class Controller {
|
||||
@Get()
|
||||
sanitizingPipe1(@Query('x', CustomSanitizingPipe) sanitized: number): string {
|
||||
return '' + sanitized; // OK
|
||||
}
|
||||
|
||||
@Get()
|
||||
sanitizingPipe2(@Query('x', new CustomSanitizingPipe()) sanitized: number): string {
|
||||
return '' + sanitized; // OK
|
||||
}
|
||||
|
||||
@Get()
|
||||
@UsePipes(CustomSanitizingPipe)
|
||||
sanitizingPipe3(@Query('x') sanitized: number): string {
|
||||
return '' + sanitized; // OK
|
||||
}
|
||||
|
||||
@Get()
|
||||
propagatingPipe1(@Query('x', CustomPropagatingPipe) unsanitized: string): string {
|
||||
return '' + unsanitized; // NOT OK
|
||||
}
|
||||
|
||||
@Get()
|
||||
propagatingPipe2(@Query('x', new CustomPropagatingPipe()) unsanitized: string): string {
|
||||
return '' + unsanitized; // NOT OK
|
||||
}
|
||||
|
||||
@Get()
|
||||
@UsePipes(CustomPropagatingPipe)
|
||||
propagatingPipe3(@Query('x') unsanitized: string): string {
|
||||
return '' + unsanitized; // NOT OK
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Get, Post, All, Query, Param, Body, Redirect, Req, Res, UploadedFile, UploadedFiles } from '@nestjs/common';
|
||||
import { SneakyQueryParam } from './customDecorator';
|
||||
|
||||
export class TestController {
|
||||
@Get('foo')
|
||||
getFoo() {
|
||||
return 'foo';
|
||||
}
|
||||
|
||||
@Post('foo')
|
||||
postFoo() {
|
||||
return 'foo';
|
||||
}
|
||||
|
||||
@Get()
|
||||
getRoot() {
|
||||
return 'foo';
|
||||
}
|
||||
|
||||
@All('bar')
|
||||
bar() {
|
||||
return 'bar';
|
||||
}
|
||||
|
||||
@Get('requestInputs/:x')
|
||||
requestInputs(
|
||||
@Param('x') x,
|
||||
@Query() queryObj,
|
||||
@Query('name') name,
|
||||
@Req() req
|
||||
) {
|
||||
if (Math.random()) return x; // NOT OK
|
||||
if (Math.random()) return queryObj; // NOT OK
|
||||
if (Math.random()) return name; // NOT OK
|
||||
if (Math.random()) return req.query.abc; // NOT OK
|
||||
return;
|
||||
}
|
||||
|
||||
@Post('post')
|
||||
post(@Body() body) {
|
||||
return body.x; // NOT OK
|
||||
}
|
||||
|
||||
@Get('redir')
|
||||
@Redirect('https://example.com')
|
||||
redir() {
|
||||
return {
|
||||
url: '//other.example.com' // OK
|
||||
};
|
||||
}
|
||||
|
||||
@Get('redir')
|
||||
@Redirect('https://example.com')
|
||||
redir2(@Query('redirect') target) {
|
||||
return {
|
||||
url: target // NOT OK
|
||||
};
|
||||
}
|
||||
|
||||
@Get()
|
||||
explicitSend(@Req() req, @Res() res) {
|
||||
res.send(req.query.x) // NOT OK
|
||||
}
|
||||
|
||||
@Post()
|
||||
upload(@UploadedFile() file) {
|
||||
return file.originalname; // NOT OK
|
||||
}
|
||||
|
||||
@Post()
|
||||
uploadMany(@UploadedFiles() files) {
|
||||
return files[0].originalname; // NOT OK
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Get, Query, UsePipes, ValidationPipe } from '@nestjs/common';
|
||||
import { IsIn } from 'class-validator';
|
||||
|
||||
export class Controller {
|
||||
@Get()
|
||||
route1(@Query('x', new ValidationPipe()) validatedObj: Struct) {
|
||||
return validatedObj.key; // OK
|
||||
}
|
||||
|
||||
@Get()
|
||||
route2(@Query('x', ValidationPipe) validatedObj: Struct) {
|
||||
return validatedObj.key; // OK
|
||||
}
|
||||
|
||||
@Get()
|
||||
@UsePipes(new ValidationPipe())
|
||||
route3(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) {
|
||||
if (Math.random()) return validatedObj.key; // OK
|
||||
return unvalidated; // NOT OK
|
||||
}
|
||||
|
||||
@Get()
|
||||
@UsePipes(ValidationPipe)
|
||||
route4(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) {
|
||||
if (Math.random()) return validatedObj.key; // OK
|
||||
return unvalidated; // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
@UsePipes(new ValidationPipe())
|
||||
export class Controller2 {
|
||||
@Get()
|
||||
route5(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) {
|
||||
if (Math.random()) return validatedObj.key; // OK
|
||||
return unvalidated; // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
@UsePipes(ValidationPipe)
|
||||
export class Controller3 {
|
||||
@Get()
|
||||
route6(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) {
|
||||
if (Math.random()) return validatedObj.key; // OK
|
||||
return unvalidated; // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
class Struct {
|
||||
@IsIn(['foo', 'bar'])
|
||||
key: string;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
routeHandler
|
||||
| global/validation.ts:6:3:9:3 | route1( ... OK\\n } |
|
||||
| local/customDecorator.ts:18:3:20:3 | sneaky( ... OK\\n } |
|
||||
| local/customDecorator.ts:23:3:25:3 | safe(@S ... OK\\n } |
|
||||
| local/customPipe.ts:20:5:22:5 | sanitiz ... K\\n } |
|
||||
| local/customPipe.ts:25:5:27:5 | sanitiz ... K\\n } |
|
||||
| local/customPipe.ts:31:5:33:5 | sanitiz ... K\\n } |
|
||||
| local/customPipe.ts:36:5:38:5 | propaga ... K\\n } |
|
||||
| local/customPipe.ts:41:5:43:5 | propaga ... K\\n } |
|
||||
| local/customPipe.ts:47:5:49:5 | propaga ... K\\n } |
|
||||
| local/routes.ts:6:3:8:3 | getFoo( ... o';\\n } |
|
||||
| local/routes.ts:11:3:13:3 | postFoo ... o';\\n } |
|
||||
| local/routes.ts:16:3:18:3 | getRoot ... o';\\n } |
|
||||
| local/routes.ts:21:3:23:3 | bar() { ... r';\\n } |
|
||||
| local/routes.ts:26:3:37:3 | request ... rn;\\n } |
|
||||
| local/routes.ts:40:3:42:3 | post(@B ... OK\\n } |
|
||||
| local/routes.ts:46:3:50:3 | redir() ... };\\n } |
|
||||
| local/routes.ts:54:3:58:3 | redir2( ... };\\n } |
|
||||
| local/routes.ts:61:3:63:3 | explici ... OK\\n } |
|
||||
| local/routes.ts:66:3:68:3 | upload( ... OK\\n } |
|
||||
| local/routes.ts:71:3:73:3 | uploadM ... OK\\n } |
|
||||
| local/validation.ts:6:3:8:3 | route1( ... OK\\n } |
|
||||
| local/validation.ts:11:3:13:3 | route2( ... OK\\n } |
|
||||
| local/validation.ts:17:3:20:3 | route3( ... OK\\n } |
|
||||
| local/validation.ts:24:3:27:3 | route4( ... OK\\n } |
|
||||
| local/validation.ts:33:3:36:3 | route5( ... OK\\n } |
|
||||
| local/validation.ts:42:3:45:3 | route6( ... OK\\n } |
|
||||
requestSource
|
||||
| local/customDecorator.ts:5:21:5:51 | ctx.swi ... quest() |
|
||||
| local/routes.ts:30:12:30:14 | req |
|
||||
| local/routes.ts:61:23:61:25 | req |
|
||||
responseSource
|
||||
| local/routes.ts:61:35:61:37 | res |
|
||||
requestInputAccess
|
||||
| body | local/routes.ts:40:16:40:19 | body |
|
||||
| body | local/routes.ts:66:26:66:29 | file |
|
||||
| body | local/routes.ts:71:31:71:35 | files |
|
||||
| parameter | global/validation.ts:6:22:6:33 | validatedObj |
|
||||
| parameter | global/validation.ts:6:56:6:66 | unvalidated |
|
||||
| parameter | local/customDecorator.ts:6:12:6:41 | request ... ryParam |
|
||||
| parameter | local/customPipe.ts:5:15:5:19 | value |
|
||||
| parameter | local/customPipe.ts:13:15:13:19 | value |
|
||||
| parameter | local/routes.ts:27:17:27:17 | x |
|
||||
| parameter | local/routes.ts:28:14:28:21 | queryObj |
|
||||
| parameter | local/routes.ts:29:20:29:23 | name |
|
||||
| parameter | local/routes.ts:35:31:35:43 | req.query.abc |
|
||||
| parameter | local/routes.ts:54:29:54:34 | target |
|
||||
| parameter | local/routes.ts:62:14:62:24 | req.query.x |
|
||||
| parameter | local/validation.ts:6:44:6:55 | validatedObj |
|
||||
| parameter | local/validation.ts:11:38:11:49 | validatedObj |
|
||||
| parameter | local/validation.ts:17:22:17:33 | validatedObj |
|
||||
| parameter | local/validation.ts:17:56:17:66 | unvalidated |
|
||||
| parameter | local/validation.ts:24:22:24:33 | validatedObj |
|
||||
| parameter | local/validation.ts:24:56:24:66 | unvalidated |
|
||||
| parameter | local/validation.ts:33:22:33:33 | validatedObj |
|
||||
| parameter | local/validation.ts:33:56:33:66 | unvalidated |
|
||||
| parameter | local/validation.ts:42:22:42:33 | validatedObj |
|
||||
| parameter | local/validation.ts:42:56:42:66 | unvalidated |
|
||||
responseSendArgument
|
||||
| global/validation.ts:7:31:7:41 | unvalidated |
|
||||
| global/validation.ts:8:12:8:27 | validatedObj.key |
|
||||
| local/customDecorator.ts:19:12:19:16 | value |
|
||||
| local/customDecorator.ts:24:12:24:16 | value |
|
||||
| local/customPipe.ts:21:16:21:29 | '' + sanitized |
|
||||
| local/customPipe.ts:26:16:26:29 | '' + sanitized |
|
||||
| local/customPipe.ts:32:16:32:29 | '' + sanitized |
|
||||
| local/customPipe.ts:37:16:37:31 | '' + unsanitized |
|
||||
| local/customPipe.ts:42:16:42:31 | '' + unsanitized |
|
||||
| local/customPipe.ts:48:16:48:31 | '' + unsanitized |
|
||||
| local/routes.ts:32:31:32:31 | x |
|
||||
| local/routes.ts:33:31:33:38 | queryObj |
|
||||
| local/routes.ts:34:31:34:34 | name |
|
||||
| local/routes.ts:35:31:35:43 | req.query.abc |
|
||||
| local/routes.ts:41:12:41:17 | body.x |
|
||||
| local/routes.ts:62:14:62:24 | req.query.x |
|
||||
| local/routes.ts:67:12:67:28 | file.originalname |
|
||||
| local/routes.ts:72:12:72:32 | files[0 ... nalname |
|
||||
| local/validation.ts:7:12:7:27 | validatedObj.key |
|
||||
| local/validation.ts:12:12:12:27 | validatedObj.key |
|
||||
| local/validation.ts:18:31:18:46 | validatedObj.key |
|
||||
| local/validation.ts:19:12:19:22 | unvalidated |
|
||||
| local/validation.ts:25:31:25:46 | validatedObj.key |
|
||||
| local/validation.ts:26:12:26:22 | unvalidated |
|
||||
| local/validation.ts:34:31:34:46 | validatedObj.key |
|
||||
| local/validation.ts:35:12:35:22 | unvalidated |
|
||||
| local/validation.ts:43:31:43:46 | validatedObj.key |
|
||||
| local/validation.ts:44:12:44:22 | unvalidated |
|
||||
redirectSink
|
||||
| local/routes.ts:48:12:48:32 | '//othe ... le.com' |
|
||||
| local/routes.ts:56:12:56:17 | target |
|
||||
19
javascript/ql/test/library-tests/frameworks/Nest/test.ql
Normal file
19
javascript/ql/test/library-tests/frameworks/Nest/test.ql
Normal file
@@ -0,0 +1,19 @@
|
||||
import javascript
|
||||
private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations
|
||||
|
||||
query HTTP::RouteHandler routeHandler() { any() }
|
||||
|
||||
query HTTP::Servers::RequestSource requestSource() { any() }
|
||||
|
||||
query HTTP::Servers::ResponseSource responseSource() { any() }
|
||||
|
||||
query RemoteFlowSource requestInputAccess(string kind) {
|
||||
kind = result.(HTTP::RequestInputAccess).getKind()
|
||||
or
|
||||
not result instanceof HTTP::RequestInputAccess and
|
||||
kind = "RemoteFlowSource"
|
||||
}
|
||||
|
||||
query HTTP::ResponseSendArgument responseSendArgument() { any() }
|
||||
|
||||
query ServerSideUrlRedirect::Sink redirectSink() { any() }
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"include": ["."]
|
||||
}
|
||||
Reference in New Issue
Block a user