Skip to content

Commit

Permalink
fix(parse-dsl): fix issue with infinite loops triggered when parsing …
Browse files Browse the repository at this point in the history
…some models (#76)

* fix: adding test for parseDSL with spaces
* fix: handle a case of an infinite loop while parsing the grammar

Co-authored-by: Raghd Hamzeh <raghd.hamzeh@auth0.com>
  • Loading branch information
adriantam and rhamzeh authored Oct 25, 2022
1 parent cc26567 commit c7629a6
Show file tree
Hide file tree
Showing 5 changed files with 360 additions and 31 deletions.
43 changes: 24 additions & 19 deletions src/openfga.ne
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
@preprocessor typescript

types -> (_newline):* (type (relations (define):+):*):* {%
types -> (_newline):* (type (_relations (_multiline_comment define):+):*):* {%
// @ts-ignore
(data) => {
// @ts-ignore
const types = data[1].map((datum) => {
const relations = datum[1][0] ? datum[1][0][1].flat() : [];
// @ts-ignore
const relations = (datum[1] && datum[1][0] && datum[1][0][1].map(innerDatum => innerDatum[1])) || [];

return { ...datum[0], relations }
})
Expand All @@ -18,35 +19,39 @@ types -> (_newline):* (type (relations (define):+):*):* {%
}
%}

type -> _multiline_comment _type _naming (_newline):+ {%
type -> _multiline_comment _type _naming (_newline):+{%
data => ({ comment: data[0], type: data[2] })
%}
relations -> _multiline_comment _relations {%
data => data[1]
relations -> _relations {%
data => data[0]
%}
define -> (_newline):+ _multiline_comment define_initial (_relation_types):? _as (define_base | define_or | define_and | define_but_not) (_newline):* {%
define -> _newline define_initial (_spacing | _relation_types) _as _spacing (define_base | define_or | define_and | define_but_not) (_newline):* {%
(data, _location, reject) => {
const relation = data[2];

const relation = data[1];
// Not supported yet
const comment = "";
const def = data[5][0];
const definition = def.type ? def :
{
type: 'single',
targets: [def]
}
const allowedTypes = data[3] ? data[3][0] : []
let allowedTypes = data[2] ? data[2][0] : [];
if (allowedTypes.length && typeof allowedTypes[0] !== "string") {
allowedTypes = [];
}

return { comment: data[1], allowedTypes, relation, definition };
return { comment, allowedTypes, relation, definition };
}
%}

define_initial -> _define _naming {%
data => data[1]
%}

define_base -> _optional_space (_naming | from_phrase) _optional_space {%
define_base -> (_naming | from_phrase) {%
data => {
const entry = data[1][0];
const entry = data[0][0];
let target, rewrite, from
if (typeof entry === "string") {
if (entry === "self") {
Expand All @@ -72,8 +77,8 @@ define_and -> define_base (_spacing _and _spacing define_base):+ {%
// @ts-ignore
data => ({ targets: [data[0], ...data[1].map((datum) => datum[3])], type: "intersection" })
%}
define_but_not -> define_base _but_not define_base {%
data => ({ base: data[0], diff: data[2], type: "exclusion" })
define_but_not -> define_base _spacing _but_not _spacing define_base {%
data => ({ base: data[0], diff: data[4], type: "exclusion" })
%}
from_phrase -> _naming _spacing _from _spacing _naming {%
data => ({ target: data[0], from: data[4] })
Expand All @@ -82,15 +87,15 @@ from_phrase -> _naming _spacing _from _spacing _naming {%
_multiline_comment -> (_comment):* {%
data => data.flat(3).join('\n')
%}
_comment -> " ":* "#" _spacing _naming (_spacing _word):* _newline {%
_comment -> (_newline):? _optional_space "#" _optional_space _word (_spacing _word):* (_newline):? {%
data => data.flat(3).join('').trim().substring(1).trim()
%}
_word -> ([a-z] | [A-Z] | [0-9] | "_" | "-" | "," | "&" | "+" | "/" | "$" ):+ _optional_space {%
data => data.flat(3).join('').trim()
%}

_relation_types -> ":" _optional_space "[" _array_of_types "]" _spacing {%
data => data[3]
_relation_types -> _optional_space ":" _optional_space "[" _array_of_types "]" _spacing {%
data => data[4]
%}

_array_of_types -> ("$"):? ([a-zA-Z0-9_#\-,\s]):+ {%
Expand All @@ -105,10 +110,10 @@ _but_not -> "but not"

_self -> "self"
_define -> " define" _spacing
_relations -> " relations" _optional_space
_relations -> " relations" (_newline):*
_type -> "type" _spacing
_no_relations -> "none" (_newline):*
_naming -> (("$"):? ( [a-z] | [A-Z] | [0-9] | "_" | "-" ):+) _optional_space {%
_naming -> (("$"):? ( [a-z] | [A-Z] | [0-9] | "_" | "-" ):+) {%
data => data.flat(3).join('').trim()
%}
_optional_space -> " ":*
Expand Down
15 changes: 12 additions & 3 deletions src/parse-dsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,19 @@ export interface ParserResult {
types: TypeDefParserResult[];
}

export const parseDSL = (code: string): ParserResult => {
export const innerParseDSL = (code: string): ParserResult[] => {
const parser = new Parser(Grammar.fromCompiled(grammar));
parser.feed(code.trim() + "\n");
return parser.results[0] || [];
const cleanedCode =
code
.split("\n")
.map((line) => line.trimEnd())
.join("\n") + "\n";
parser.feed(cleanedCode);
return parser.results;
};

export const parseDSL = (code: string): ParserResult => {
return innerParseDSL(code)[0] || [];
};

// The TransformedType allows us to quickly access the various relations unique by
Expand Down
8 changes: 4 additions & 4 deletions tests/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ exports[`apiSyntaxToFriendlySyntax should correctly read from \`or\` definition
"
`;

exports[`apiSyntaxToFriendlySyntax should read a complex definition 1`] = `
exports[`apiSyntaxToFriendlySyntax should read a complex definition 4 1`] = `
"type folder
relations
define deleter as self
Expand Down Expand Up @@ -212,7 +212,7 @@ exports[`friendlySyntaxToApiSyntax() should correctly read from \`or\` definitio
}
`;

exports[`friendlySyntaxToApiSyntax() should read a complex definition 1`] = `
exports[`friendlySyntaxToApiSyntax() should read a complex definition 1 1`] = `
{
"schema_version": "1.0",
"type_definitions": [
Expand Down Expand Up @@ -327,7 +327,7 @@ exports[`friendlySyntaxToApiSyntax() should read a complex definition 1`] = `
}
`;

exports[`friendlySyntaxToApiSyntax() should read a complex definition 2`] = `
exports[`friendlySyntaxToApiSyntax() should read a complex definition 2 1`] = `
{
"schema_version": "1.0",
"type_definitions": [
Expand Down Expand Up @@ -415,7 +415,7 @@ exports[`friendlySyntaxToApiSyntax() should read a complex definition 2`] = `
}
`;

exports[`friendlySyntaxToApiSyntax() should read a complex definition 3`] = `
exports[`friendlySyntaxToApiSyntax() should read a complex definition 3 1`] = `
{
"schema_version": "1.0",
"type_definitions": [
Expand Down
Loading

0 comments on commit c7629a6

Please sign in to comment.