Skip to content

Commit

Permalink
feat: Added new lenient flags to allow spaces after chunk header.
Browse files Browse the repository at this point in the history
  • Loading branch information
ShogunPanda committed Sep 12, 2023
1 parent 998bcc5 commit c373868
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 4 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,15 @@ With this flag the new chunk can start immediately after the previous one.
**Enabling this flag can pose a security issue since you will be exposed to request smuggling attacks. USE WITH CAUTION!**
### `void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled)`
Enables/disables lenient handling of spaces after chunk size.
Normally `llhttp` would error when after a chunk size is followed by one or more spaces are present instead of a CRLF or `;`.
With this flag this check is disabled.
**Enabling this flag can pose a security issue since you will be exposed to request smuggling attacks. USE WITH CAUTION!**
## Build Instructions
Make sure you have [Node.js](https://nodejs.org/), npm and npx installed. Then under project directory run:
Expand Down
1 change: 1 addition & 0 deletions src/llhttp/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export enum LENIENT_FLAGS {
OPTIONAL_LF_AFTER_CR = 1 << 6,
OPTIONAL_CRLF_AFTER_CHUNK = 1 << 7,
OPTIONAL_CR_BEFORE_LF = 1 << 8,
SPACES_AFTER_CHUNK_SIZE = 1 << 9,
}

export enum METHODS {
Expand Down
10 changes: 10 additions & 0 deletions src/llhttp/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,16 @@ export class HTTP {
.otherwise(n('chunk_size_otherwise'));

n('chunk_size_otherwise')
.match(
[ ' ', '\t' ],
this.testLenientFlags(
LENIENT_FLAGS.SPACES_AFTER_CHUNK_SIZE,
{
1: n('chunk_size_otherwise'),
},
p.error(ERROR.INVALID_CHUNK_SIZE, 'Invalid character in chunk size'),
),
)
.match('\r', n('chunk_size_almost_done'))
.match(
'\n',
Expand Down
8 changes: 8 additions & 0 deletions src/native/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,14 @@ void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) {
}
}

void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) {
if (enabled) {
parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;
} else {
parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE;
}
}

/* Callbacks */


Expand Down
10 changes: 10 additions & 0 deletions test/fixtures/extra.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ void llhttp__test_init_response_lenient_optional_crlf_after_chunk(llparse_t* s)
s->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
}

void llhttp__test_init_request_lenient_spaces_after_chunk_size(llparse_t* s) {
llhttp__test_init_request(s);
s->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;
}

void llhttp__test_init_response_lenient_spaces_after_chunk_size(llparse_t* s) {
llhttp__test_init_response(s);
s->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;
}


void llhttp__test_finish(llparse_t* s) {
llparse__print(NULL, NULL, "finish=%d", s->finish);
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type TestType = 'request' | 'response' | 'request-finish' | 'response-fin
'request-lenient-optional-lf-after-cr' | 'response-lenient-optional-lf-after-cr' |
'request-lenient-optional-cr-before-lf' | 'response-lenient-optional-cr-before-lf' |
'request-lenient-optional-crlf-after-chunk' | 'response-lenient-optional-crlf-after-chunk' |
'request-lenient-spaces-after-chunk-size' | 'response-lenient-spaces-after-chunk-size' |
'none' | 'url';

export const allowedTypes: TestType[] = [
Expand All @@ -45,6 +46,8 @@ export const allowedTypes: TestType[] = [
'response-lenient-optional-cr-before-lf',
'request-lenient-optional-crlf-after-chunk',
'response-lenient-optional-crlf-after-chunk',
'request-lenient-spaces-after-chunk-size',
'response-lenient-spaces-after-chunk-size',
];

const BUILD_DIR = path.join(__dirname, '..', 'tmp');
Expand Down
67 changes: 65 additions & 2 deletions test/request/transfer-encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ off=83 header_field complete
off=84 len=7 span[header_value]="chunked"
off=93 header_value complete
off=95 headers complete method=3 v=1/1 flags=208 content_length=0
off=96 error code=12 reason="Invalid character in chunk size"
off=97 error code=12 reason="Invalid character in chunk size"
```

### No extension after semicolon
Expand Down Expand Up @@ -884,7 +884,7 @@ off=37 header_field complete
off=38 len=7 span[header_value]="chunked"
off=47 header_value complete
off=49 headers complete method=4 v=1/1 flags=208 content_length=0
off=50 error code=12 reason="Invalid character in chunk size"
off=51 error code=12 reason="Invalid character in chunk size"
```

## Invalid OBS fold after chunked value
Expand Down Expand Up @@ -1117,4 +1117,67 @@ off=79 chunk header len=5
off=79 len=5 span[body]="ABCDE"
off=84 chunk complete
off=87 chunk header len=0
```

## Space after chunk header

<!-- meta={"type": "request"} -->
```http
PUT /url HTTP/1.1
Transfer-Encoding: chunked
a \r\n0123456789
0
```

```log
off=0 message begin
off=0 len=3 span[method]="PUT"
off=3 method complete
off=4 len=4 span[url]="/url"
off=9 url complete
off=14 len=3 span[version]="1.1"
off=17 version complete
off=19 len=17 span[header_field]="Transfer-Encoding"
off=37 header_field complete
off=38 len=7 span[header_value]="chunked"
off=47 header_value complete
off=49 headers complete method=4 v=1/1 flags=208 content_length=0
off=51 error code=12 reason="Invalid character in chunk size"
```

## Space after chunk header (lenient)

<!-- meta={"type": "request-lenient-spaces-after-chunk-size"} -->
```http
PUT /url HTTP/1.1
Transfer-Encoding: chunked
a \r\n0123456789
0
```

```log
off=0 message begin
off=0 len=3 span[method]="PUT"
off=3 method complete
off=4 len=4 span[url]="/url"
off=9 url complete
off=14 len=3 span[version]="1.1"
off=17 version complete
off=19 len=17 span[header_field]="Transfer-Encoding"
off=37 header_field complete
off=38 len=7 span[header_value]="chunked"
off=47 header_value complete
off=49 headers complete method=4 v=1/1 flags=208 content_length=0
off=53 chunk header len=10
off=53 len=10 span[body]="0123456789"
off=65 chunk complete
off=68 chunk header len=0
off=70 chunk complete
off=70 message complete
```
4 changes: 2 additions & 2 deletions test/response/transfer-encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ off=61 header_field complete
off=62 len=7 span[header_value]="chunked"
off=71 header_value complete
off=73 headers complete status=200 v=1/1 flags=208 content_length=0
off=75 error code=12 reason="Invalid character in chunk size"
off=76 error code=12 reason="Invalid character in chunk size"
```

## `chunked` before other transfer-encoding
Expand Down Expand Up @@ -229,7 +229,7 @@ off=52 header_field complete
off=53 len=7 span[header_value]="chunked"
off=62 header_value complete
off=64 headers complete status=200 v=1/1 flags=208 content_length=0
off=65 error code=12 reason="Invalid character in chunk size"
off=66 error code=12 reason="Invalid character in chunk size"
```


Expand Down

0 comments on commit c373868

Please sign in to comment.