Merge pull request #3178 from pietrygamat/bugfix/571

fix: unable to set request bodies with colon characters in their names
This commit is contained in:
Anoop M D
2025-08-30 01:57:54 +05:30
committed by GitHub
5 changed files with 185 additions and 10 deletions

View File

@@ -56,7 +56,13 @@ const grammar = ohm.grammar(`Bru {
// Dictionary Blocks
dictionary = st* "{" pairlist? tagend
pairlist = optionalnl* pair (~tagend stnl* pair)* (~tagend space)*
pair = st* key st* ":" st* value st*
pair = st* (quoted_key | key) st* ":" st* value st*
disable_char = "~"
quote_char = "\\""
esc_char = "\\\\"
esc_quote_char = esc_char quote_char
quoted_key_char = ~(quote_char | esc_quote_char | nl) any
quoted_key = disable_char? quote_char (esc_quote_char | quoted_key_char)* quote_char
key = keychar*
value = list | multilinetextblock | valuechar*
@@ -301,6 +307,14 @@ const sem = grammar.createSemantics().addAttribute('ast', {
res[key.ast] = value.ast ? value.ast.trim() : '';
return res;
},
esc_quote_char(_1, quote) {
// unescape
return quote.sourceString;
},
quoted_key(disabled, _1, chars, _2) {
// unquote
return (disabled? disabled.sourceString : "") + chars.ast.join("");
},
key(chars) {
return chars.sourceString ? chars.sourceString.trim() : '';
},
@@ -364,6 +378,9 @@ const sem = grammar.createSemantics().addAttribute('ast', {
tagend(_1, _2) {
return '';
},
_terminal(){
return this.sourceString;
},
multilinetextblockdelimiter(_) {
return '';
},

View File

@@ -4,6 +4,10 @@ const { indentString } = require('./utils');
const enabled = (items = [], key = "enabled") => items.filter((item) => item[key]);
const disabled = (items = [], key = "enabled") => items.filter((item) => !item[key]);
const quoteKey = (key) => {
const quotableChars = [':', '"', '{', '}', ' '];
return quotableChars.some(char => key.includes(char)) ? ('"' + key.replaceAll('"', '\\"') + '"') : key;
}
// remove the last line if two new lines are found
const stripLastLine = (text) => {
@@ -121,7 +125,7 @@ const jsonToBru = (json) => {
if (enabled(queryParams).length) {
bru += `\n${indentString(
enabled(queryParams)
.map((item) => `${item.name}: ${item.value}`)
.map((item) => `${quoteKey(item.name)}: ${item.value}`)
.join('\n')
)}`;
}
@@ -129,7 +133,7 @@ const jsonToBru = (json) => {
if (disabled(queryParams).length) {
bru += `\n${indentString(
disabled(queryParams)
.map((item) => `~${item.name}: ${item.value}`)
.map((item) => `~${quoteKey(item.name)}: ${item.value}`)
.join('\n')
)}`;
}
@@ -151,7 +155,7 @@ const jsonToBru = (json) => {
if (enabled(headers).length) {
bru += `\n${indentString(
enabled(headers)
.map((item) => `${item.name}: ${item.value}`)
.map((item) => `${quoteKey(item.name)}: ${item.value}`)
.join('\n')
)}`;
}
@@ -159,7 +163,7 @@ const jsonToBru = (json) => {
if (disabled(headers).length) {
bru += `\n${indentString(
disabled(headers)
.map((item) => `~${item.name}: ${item.value}`)
.map((item) => `~${quoteKey(item.name)}: ${item.value}`)
.join('\n')
)}`;
}
@@ -246,7 +250,7 @@ ${indentString(`domain: ${auth?.ntlm?.domain || ''}`)}
}
`;
}
}
if (auth && auth.oauth2) {
switch (auth?.oauth2?.grantType) {
@@ -496,14 +500,14 @@ ${indentString(body.sparql)}
if (enabled(body.formUrlEncoded).length) {
const enabledValues = enabled(body.formUrlEncoded)
.map((item) => `${item.name}: ${getValueString(item.value)}`)
.map((item) => `${quoteKey(item.name)}: ${getValueString(item.value)}`)
.join('\n');
bru += `${indentString(enabledValues)}\n`;
}
if (disabled(body.formUrlEncoded).length) {
const disabledValues = disabled(body.formUrlEncoded)
.map((item) => `~${item.name}: ${getValueString(item.value)}`)
.map((item) => `~${quoteKey(item.name)}: ${getValueString(item.value)}`)
.join('\n');
bru += `${indentString(disabledValues)}\n`;
}
@@ -524,7 +528,7 @@ ${indentString(body.sparql)}
item.contentType && item.contentType !== '' ? ' @contentType(' + item.contentType + ')' : '';
if (item.type === 'text') {
return `${enabled}${item.name}: ${getValueString(item.value)}${contentType}`;
return `${enabled}${quoteKey(item.name)}: ${getValueString(item.value)}${contentType}`;
}
if (item.type === 'file') {
@@ -532,7 +536,7 @@ ${indentString(body.sparql)}
const filestr = filepaths.join('|');
const value = `@file(${filestr})`;
return `${enabled}${item.name}: ${value}${contentType}`;
return `${enabled}${quoteKey(item.name)}: ${value}${contentType}`;
}
})
.join('\n')

View File

@@ -81,6 +81,25 @@ headers {
expect(output).toEqual(expected);
});
it('should parse single header with empty key', () => {
const input = `
headers {
: world
}`;
const output = parser(input);
const expected = {
headers: [
{
name: '',
value: 'world',
enabled: true
}
]
};
expect(output).toEqual(expected);
});
it('should parse multi headers', () => {
const input = `
headers {

View File

@@ -17,6 +17,11 @@ get {
params:query {
apiKey: secret
numbers: 998877665
"key with spaces": is allowed
"colon:parameter": is allowed
"nested escaped \"quote\"": is allowed
"{braces}": is allowed
~"disabled:colon:parameter": is allowed
~message: hello
}
@@ -27,6 +32,11 @@ params:path {
headers {
content-type: application/json
Authorization: Bearer 123
"key with spaces": is allowed
"colon:header": is allowed
"{braces}": is allowed
"nested escaped \"quote\"": is allowed
~"disabled:colon:header": is allowed
~transaction-id: {{transactionId}}
}
@@ -104,13 +114,23 @@ body:sparql {
body:form-urlencoded {
apikey: secret
numbers: +91998877665
"key with spaces": is allowed
"colon:parameter": is allowed
"nested escaped \"quote\"": is allowed
"{braces}": is allowed
~message: hello
~"disabled colon:parameter": is allowed
}
body:multipart-form {
apikey: secret
numbers: +91998877665
"key with spaces": is allowed
"colon:part": is allowed
"nested escaped \"quote\"": is allowed
"{braces}": is allowed
~message: hello
~"disabled colon:part": is allowed
}
body:file {

View File

@@ -24,6 +24,36 @@
"type": "query",
"enabled": true
},
{
"name": "key with spaces",
"value": "is allowed",
"type": "query",
"enabled": true
},
{
"name" : "colon:parameter",
"value" : "is allowed",
"type": "query",
"enabled": true
},
{
"name" : "nested escaped \"quote\"",
"value" : "is allowed",
"type": "query",
"enabled": true
},
{
"name": "{braces}",
"value": "is allowed",
"type": "query",
"enabled": true
},
{
"name" : "disabled:colon:parameter",
"value" : "is allowed",
"type": "query",
"enabled": false
},
{
"name": "message",
"value": "hello",
@@ -48,6 +78,31 @@
"value": "Bearer 123",
"enabled": true
},
{
"name": "key with spaces",
"value": "is allowed",
"enabled": true
},
{
"name": "colon:header",
"value": "is allowed",
"enabled": true
},
{
"name": "{braces}",
"value": "is allowed",
"enabled": true
},
{
"name": "nested escaped \"quote\"",
"value": "is allowed",
"enabled": true
},
{
"name": "disabled:colon:header",
"value": "is allowed",
"enabled": false
},
{
"name": "transaction-id",
"value": "{{transactionId}}",
@@ -118,10 +173,35 @@
"value": "+91998877665",
"enabled": true
},
{
"name": "key with spaces",
"value": "is allowed",
"enabled": true
},
{
"name": "colon:parameter",
"value": "is allowed",
"enabled": true
},
{
"name": "nested escaped \"quote\"",
"value": "is allowed",
"enabled": true
},
{
"name": "{braces}",
"value": "is allowed",
"enabled": true
},
{
"name": "message",
"value": "hello",
"enabled": false
},
{
"name": "disabled colon:parameter",
"value": "is allowed",
"enabled": false
}
],
"multipartForm": [
@@ -139,12 +219,47 @@
"enabled": true,
"type": "text"
},
{
"contentType": "",
"name": "key with spaces",
"value": "is allowed",
"enabled": true,
"type": "text"
},
{
"contentType": "",
"name": "colon:part",
"value": "is allowed",
"enabled": true,
"type": "text"
},
{
"contentType": "",
"name": "nested escaped \"quote\"",
"value": "is allowed",
"enabled": true,
"type": "text"
},
{
"contentType": "",
"name": "{braces}",
"value": "is allowed",
"enabled": true,
"type": "text"
},
{
"contentType": "",
"name": "message",
"value": "hello",
"enabled": false,
"type": "text"
},
{
"contentType": "",
"name": "disabled colon:part",
"value": "is allowed",
"enabled": false,
"type": "text"
}
],
"file" : [