guard.ts: cover every branch for a rule Go's regexp reads otherwise, and hold both matchers to one table (issue 539)
mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
Review 30 found guard.ts missing branches the forge protects: Go reads \d, \w, \pL, a class opening with ] or ^, and [[:digit:]] otherwise than JavaScript. A rule using any of them now covers every branch, so the guard refuses more than the forge, never less. test/rule-cases.json holds the forge's answers, read by the Go and TypeScript tests alike.
This commit is contained in:
@@ -398,24 +398,27 @@ func TestACommitAskThatCannotBeReadIsRefused(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuleCoversIsTheGuardsRule(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
rule, branch string
|
||||
want bool
|
||||
}{
|
||||
{"main", "main", true}, {"main", "mainx", false}, {"release/*", "release/1.0", true},
|
||||
{"release/*", "release/a/b", false}, {"release/**", "release/a/b", true}, {"v?", "v1", true},
|
||||
{"a.b", "axb", false},
|
||||
// As the forge decides it (novox/hq issue 539): classes, alternatives, escapes, a plain name in any case.
|
||||
{"release-[ab]", "release-a", true}, {"release-[ab]", "release-c", false}, {"release-[!ab]", "release-c", true},
|
||||
{"v[0-9].x", "v7.x", true}, {"v[0-9].x", "vA.x", false}, {"{main,master}", "master", true},
|
||||
{"{main,master}", "mainline", false}, {"rel/{a,b}/*", "rel/b/1", true}, {"rel/{a,b}/*", "rel/c/1", false},
|
||||
{"Main", "main", true}, {"main", "MAIN", true}, {"Feat/*", "feat/x", false}, {`a\*`, "a*", true},
|
||||
{`a\*`, "ab", false}, {"a+b", "a+b", true}, {"a+b", "aab", false}, {"{open", "{open", true},
|
||||
{"[open", "[open", true}, {"[open", "o", false},
|
||||
} {
|
||||
if got := ruleCovers(c.rule, c.branch); got != c.want {
|
||||
t.Errorf("%s covers %s: %v", c.rule, c.branch, got)
|
||||
// The forge's own answers (Gitea 1.27's Match), one table for this bundle and guard.ts alike (novox/hq issue 539).
|
||||
// `forge` is the forge's answer, which this port must give; `guard` is where guard.ts deliberately covers more.
|
||||
func TestRuleCoversAnswersAsTheForgeDoes(t *testing.T) {
|
||||
raw, err := os.ReadFile("../../test/rule-cases.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var cases []struct {
|
||||
Rule string `json:"rule"`
|
||||
Branch string `json:"branch"`
|
||||
Forge bool `json:"forge"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &cases); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(cases) < 100 {
|
||||
t.Fatalf("only %d cases", len(cases))
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := ruleCovers(c.Rule, c.Branch); got != c.Forge {
|
||||
t.Errorf("%q covers %q: %v, the forge says %v", c.Rule, c.Branch, got, c.Forge)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-3
@@ -170,20 +170,37 @@ export function judgeApiCall(method: string, rawPath: string, body: unknown): Ap
|
||||
* covers the branch of that name in any case; any other name is a glob with `/` as its separator — `*` any run
|
||||
* within a segment, `**` any run across them, `?` one character of a segment, `[abc]`, `[a-z]` and `[!abc]` one
|
||||
* character, `{a,b}` either, `\` the next character itself — matched case-sensitively; a glob the forge cannot
|
||||
* compile covers its own text exactly. The forge bundle's ruleCovers (cmd/forge/rules.go) is the same rule.
|
||||
* compile covers its own text exactly. The forge bundle's ruleCovers (cmd/forge/rules.go) is the same rule, and
|
||||
* both are held to one table of answers (test/rule-cases.json).
|
||||
*
|
||||
* The forge hands the compiled glob to Go's regexp, which reads some things JavaScript's reads otherwise: a
|
||||
* backslash before a letter or digit (`\d`, `\w`, `\pL`, `\b`, `\A` are classes or anchors there), a class
|
||||
* that opens with `]` (`[]a]` holds `]` and `a` there, an empty class here) or with `^` (a negation there),
|
||||
* and a POSIX class (`[[:digit:]]`).
|
||||
* A rule using any of them is taken to cover every branch: refusing a write the forge would take is safe,
|
||||
* taking one it guards is not.
|
||||
*/
|
||||
export function ruleCovers(rule: string, branch: string): boolean {
|
||||
if (!/[*?\\[\]{}]/.test(rule)) return rule.toLowerCase() === branch.toLowerCase();
|
||||
if (goReadsOtherwise(rule)) return true;
|
||||
const re = globRegExp(rule);
|
||||
return re ? re.test(branch) : rule === branch;
|
||||
}
|
||||
|
||||
/** Whether a glob holds what Go's regexp reads otherwise than JavaScript's (see ruleCovers): fail closed. */
|
||||
function goReadsOtherwise(rule: string): boolean {
|
||||
return /\\[\p{L}\p{N}]/u.test(rule) || /\[!?[\]^]/.test(rule) || /\[[^\]]*\[:/.test(rule);
|
||||
}
|
||||
|
||||
/** The forge's glob as a regular expression, compiled as its modules/glob compiles it with `/` as separator. */
|
||||
function globRegExp(glob: string): RegExp | null {
|
||||
const g = Array.from(glob);
|
||||
let out = "";
|
||||
let pos = 0;
|
||||
// A character as itself: escaped when it is syntax to a regular expression, and only then — the `u` flag
|
||||
// refuses any other escape, and `-` may be escaped only inside a class.
|
||||
const lit = (c: string) => c.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
|
||||
const litInClass = (c: string) => c.replace(/[\]\\[^-]/g, "\\$&");
|
||||
const chars = (): boolean => {
|
||||
out += "[";
|
||||
if (g[pos] === "!") {
|
||||
@@ -198,8 +215,8 @@ function globRegExp(glob: string): RegExp | null {
|
||||
}
|
||||
if (c === "\\") {
|
||||
if (pos >= g.length) return false;
|
||||
out += "\\" + g[pos++];
|
||||
} else out += c === "[" ? "\\[" : c;
|
||||
out += litInClass(g[pos++]);
|
||||
} else out += c === "[" || c === "^" ? "\\" + c : c;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { test, after, beforeEach } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
import { GiteaClient } from "../dist/client.js";
|
||||
import { ConfiguredToken } from "../dist/token.js";
|
||||
@@ -147,17 +148,15 @@ test("gitea_api refuses a file written to the default branch or one a rule guard
|
||||
});
|
||||
|
||||
test("a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539)", async () => {
|
||||
const cases: Array<[string, string, boolean]> = [
|
||||
["main", "main", true], ["main", "mainx", false], ["release/*", "release/1.0", true], ["release/*", "release/a/b", false],
|
||||
["release/**", "release/a/b", true], ["v?", "v1", true], ["a.b", "axb", false],
|
||||
["release-[ab]", "release-a", true], ["release-[ab]", "release-c", false], ["release-[!ab]", "release-c", true],
|
||||
["v[0-9].x", "v7.x", true], ["v[0-9].x", "vA.x", false], ["{main,master}", "master", true],
|
||||
["{main,master}", "mainline", false], ["rel/{a,b}/*", "rel/b/1", true], ["rel/{a,b}/*", "rel/c/1", false],
|
||||
["Main", "main", true], ["main", "MAIN", true], ["Feat/*", "feat/x", false], ["a\\*", "a*", true],
|
||||
["a\\*", "ab", false], ["a+b", "a+b", true], ["a+b", "aab", false], ["{open", "{open", true],
|
||||
["[open", "[open", true], ["[open", "o", false],
|
||||
];
|
||||
for (const [rule, branch, want] of cases) assert.equal(ruleCovers(rule, branch), want, `${rule} covers ${branch}`);
|
||||
// One table with the forge bundle (cmd/forge/rules.go): `forge` is the forge's answer; `guard` is where this
|
||||
// guard deliberately covers more — a rule Go's regexp reads otherwise than JavaScript's — and never less.
|
||||
const cases = JSON.parse(readFileSync(new URL("./rule-cases.json", import.meta.url), "utf8")) as
|
||||
Array<{ rule: string; branch: string; forge: boolean; guard?: boolean }>;
|
||||
assert.ok(cases.length >= 100);
|
||||
for (const c of cases) {
|
||||
assert.ok(c.guard === undefined || (c.guard && !c.forge), `${c.rule}: the guard may only cover more than the forge`);
|
||||
assert.equal(ruleCovers(c.rule, c.branch), c.guard ?? c.forge, `${c.rule} covers ${c.branch}`);
|
||||
}
|
||||
const put = (branch: string) => tool("gitea_api").run({ path: "/repos/novox/mesh-catalog/contents/README.md",
|
||||
method: "PUT", body: { content: "e30=", sha: "x", branch } });
|
||||
state.rules.push({ rule_name: "release-[ab]" }, { rule_name: "{stable,lts}/*" });
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
[
|
||||
{"rule":"main","branch":"main","forge":true,"why":"a plain name covers its branch"},
|
||||
{"rule":"main","branch":"mainx","forge":false},
|
||||
{"rule":"main","branch":"MAIN","forge":true,"why":"a plain name covers in any case (EqualFold)"},
|
||||
{"rule":"Main","branch":"main","forge":true},
|
||||
{"rule":"release/*","branch":"release/1.0","forge":true,"why":"* stays within a segment"},
|
||||
{"rule":"release/*","branch":"release/a/b","forge":false},
|
||||
{"rule":"release/**","branch":"release/a/b","forge":true,"why":"** crosses segments"},
|
||||
{"rule":"v?","branch":"v1","forge":true},
|
||||
{"rule":"v?","branch":"v/","forge":false,"why":"? is not the separator"},
|
||||
{"rule":"a.b","branch":"axb","forge":false,"why":". is itself"},
|
||||
{"rule":"Feat/*","branch":"feat/x","forge":false,"why":"a glob is matched with case"},
|
||||
{"rule":"release-[ab]","branch":"release-a","forge":true,"why":"a class"},
|
||||
{"rule":"release-[ab]","branch":"release-c","forge":false},
|
||||
{"rule":"release-[!ab]","branch":"release-c","forge":true,"why":"a negated class"},
|
||||
{"rule":"release-[!ab]","branch":"release-a","forge":false},
|
||||
{"rule":"v[0-9].x","branch":"v7.x","forge":true,"why":"a range"},
|
||||
{"rule":"v[0-9].x","branch":"vA.x","forge":false},
|
||||
{"rule":"{main,master}","branch":"master","forge":true,"why":"alternatives"},
|
||||
{"rule":"{main,master}","branch":"mainline","forge":false},
|
||||
{"rule":"rel/{a,b}/*","branch":"rel/b/1","forge":true},
|
||||
{"rule":"rel/{a,b}/*","branch":"rel/c/1","forge":false},
|
||||
{"rule":"a\\*","branch":"a*","forge":true,"why":"an escape"},
|
||||
{"rule":"a\\*","branch":"ab","forge":false},
|
||||
{"rule":"a+b","branch":"a+b","forge":true,"why":"+ is itself"},
|
||||
{"rule":"a+b","branch":"aab","forge":false},
|
||||
{"rule":"{open","branch":"{open","forge":true,"why":"a glob the forge cannot compile covers its own text"},
|
||||
{"rule":"[open","branch":"[open","forge":true},
|
||||
{"rule":"[open","branch":"o","forge":false},
|
||||
{"rule":"release-\\d","branch":"release-1","forge":true,"why":"Go reads \\d as a digit"},
|
||||
{"rule":"release-\\d","branch":"release-d","forge":false,"guard":true},
|
||||
{"rule":"v\\w*","branch":"v2","forge":true},
|
||||
{"rule":"main\\s","branch":"main","forge":false,"guard":true},
|
||||
{"rule":"[]a]","branch":"a","forge":true,"why":"a class opening with ] holds ]"},
|
||||
{"rule":"[]a]","branch":"]","forge":true},
|
||||
{"rule":"[]a]","branch":"b","forge":false,"guard":true},
|
||||
{"rule":"rel-[\\d]","branch":"rel-1","forge":true},
|
||||
{"rule":"v[\\pL]","branch":"va","forge":true,"why":"\\p is a Unicode class"},
|
||||
{"rule":"v\\pL","branch":"va","forge":true},
|
||||
{"rule":"[[:alpha:]]","branch":"a","forge":true,"why":"a POSIX class"},
|
||||
{"rule":"rel-[[:digit:]]","branch":"rel-1","forge":true},
|
||||
{"rule":"rel-[[:digit:]]","branch":"rel-x","forge":false,"guard":true},
|
||||
{"rule":"[^+]*","branch":"-[1","forge":true,"why":"a class opening with ^ is negated in Go"},
|
||||
{"rule":"d[^](","branch":"d[^](","forge":true},
|
||||
{"rule":"]?","branch":":,1},","forge":false},
|
||||
{"rule":"Ab,","branch":"Ab,","forge":true},
|
||||
{"rule":"\\:[}-|","branch":",{","forge":false},
|
||||
{"rule":"^,*.d::","branch":"/x","forge":false},
|
||||
{"rule":"","branch":"A*:","forge":false},
|
||||
{"rule":"!|A{","branch":":.{]","forge":false},
|
||||
{"rule":"","branch":"]éb","forge":false},
|
||||
{"rule":"/{)w","branch":"/{)w","forge":true},
|
||||
{"rule":"*^w]","branch":"}!.","forge":false},
|
||||
{"rule":"*),^?*d","branch":"*),^?*d","forge":true},
|
||||
{"rule":"A)^}!","branch":"","forge":false},
|
||||
{"rule":"}^d?","branch":"1\\.","forge":false},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"$?[","branch":"$?[","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"+","branch":"+","forge":true},
|
||||
{"rule":"","branch":"},[","forge":false},
|
||||
{"rule":"xA[d:a)","branch":"]1.!bx","forge":false},
|
||||
{"rule":"^/}(","branch":"","forge":false},
|
||||
{"rule":"-|x.x1?","branch":"-|x.x1?","forge":true},
|
||||
{"rule":"x+)A]-A","branch":"x+)A]-A","forge":true},
|
||||
{"rule":"x*a","branch":"x*a","forge":true},
|
||||
{"rule":"$xb","branch":"}é","forge":false},
|
||||
{"rule":"wAx?/a,","branch":"","forge":false},
|
||||
{"rule":"[,","branch":"[,","forge":true},
|
||||
{"rule":"}!","branch":"}!","forge":true},
|
||||
{"rule":"*:|\\w","branch":"","forge":false,"guard":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"é","branch":"é","forge":true},
|
||||
{"rule":"..(","branch":"{\\{+","forge":false},
|
||||
{"rule":"*|{","branch":"1.}-","forge":false},
|
||||
{"rule":"!+,","branch":"}++-*","forge":false},
|
||||
{"rule":"x\\({","branch":"*","forge":false},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"?-{+-","branch":"?-{+-","forge":true},
|
||||
{"rule":"/-{é|$","branch":"b!.[","forge":false},
|
||||
{"rule":"a-+dxA","branch":"1AA,xA","forge":false},
|
||||
{"rule":"]|(","branch":"]|(","forge":true},
|
||||
{"rule":"}","branch":"{!x{d","forge":false},
|
||||
{"rule":"/{*](","branch":"!]","forge":false},
|
||||
{"rule":"($+{-}|","branch":"/","forge":false},
|
||||
{"rule":"(-b.-1}","branch":"*A*+,1","forge":false},
|
||||
{"rule":"1[d|)wa","branch":"+}[!}}","forge":false},
|
||||
{"rule":"w","branch":":.","forge":false},
|
||||
{"rule":"?}\\*","branch":"","forge":false},
|
||||
{"rule":"?$*","branch":"?$*","forge":true},
|
||||
{"rule":"","branch":"!/{béa","forge":false},
|
||||
{"rule":"","branch":"A1","forge":false},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"!a$1/b{","branch":"!a$1/b{","forge":true},
|
||||
{"rule":":dww/|d","branch":":dww/|d","forge":true},
|
||||
{"rule":"!|*}","branch":"xéxé{b","forge":false},
|
||||
{"rule":"1é||","branch":"a+.","forge":false},
|
||||
{"rule":"/+],","branch":"!-\\[{é","forge":false},
|
||||
{"rule":"a}(","branch":"a}(","forge":true},
|
||||
{"rule":"|A?.","branch":"}.","forge":false},
|
||||
{"rule":"d$a^d","branch":"*{","forge":false},
|
||||
{"rule":"-ba$+A","branch":"-ba$+A","forge":true},
|
||||
{"rule":"-.|,?a!","branch":"-.|,?a!","forge":true},
|
||||
{"rule":"A","branch":"A","forge":true},
|
||||
{"rule":"w","branch":"w","forge":true},
|
||||
{"rule":".!1!+a","branch":".!1!+a","forge":true},
|
||||
{"rule":"+$w:}","branch":"+$w:}","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":")]","branch":")]","forge":true},
|
||||
{"rule":"a})^,}1","branch":"a})^,}1","forge":true},
|
||||
{"rule":":[:!:","branch":":[:!:","forge":true},
|
||||
{"rule":"w?w:]1!","branch":"\\.]-","forge":false},
|
||||
{"rule":"*$/","branch":"*$/","forge":true},
|
||||
{"rule":"é](","branch":"é](","forge":true},
|
||||
{"rule":"x$a/","branch":"x$a/","forge":true},
|
||||
{"rule":"*)b+[^","branch":"*)b+[^","forge":true},
|
||||
{"rule":"/]/?","branch":"/]/?","forge":true},
|
||||
{"rule":"{x,A","branch":"!Ab","forge":false},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"-,","branch":"xé}A[.","forge":false},
|
||||
{"rule":"w)Ad","branch":"w)Ad","forge":true},
|
||||
{"rule":":??","branch":":??","forge":true},
|
||||
{"rule":"x/xé|","branch":"x/xé|","forge":true},
|
||||
{"rule":"","branch":"1!","forge":false},
|
||||
{"rule":"!![é[","branch":"x","forge":false},
|
||||
{"rule":"wb$","branch":"wb$","forge":true},
|
||||
{"rule":")","branch":")","forge":true},
|
||||
{"rule":"Aw:)(1","branch":"{b\\,","forge":false},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"1*}?d","branch":"1*}?d","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"?-b.","branch":"?-b.","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"d","branch":",aé1","forge":false},
|
||||
{"rule":"+","branch":"+{","forge":false},
|
||||
{"rule":"]d","branch":"]}:/bA","forge":false},
|
||||
{"rule":"*?+:|","branch":"!é.","forge":false},
|
||||
{"rule":"}$d","branch":"}$d","forge":true},
|
||||
{"rule":"/","branch":"/","forge":true},
|
||||
{"rule":"a$+b)b","branch":"d}","forge":false},
|
||||
{"rule":"?$[/A","branch":"","forge":false},
|
||||
{"rule":"é){","branch":"é){","forge":true},
|
||||
{"rule":"$(d","branch":"{é}","forge":false},
|
||||
{"rule":"b(","branch":"b(","forge":true},
|
||||
{"rule":"^^+]","branch":"^^+]","forge":true},
|
||||
{"rule":",)","branch":",)","forge":true},
|
||||
{"rule":"d-.A-","branch":"d-.A-","forge":true},
|
||||
{"rule":"$","branch":"\\/","forge":false},
|
||||
{"rule":"}+[\\|\\$","branch":"x[!b","forge":false},
|
||||
{"rule":"(d1aé!é","branch":"A[-","forge":false},
|
||||
{"rule":"x","branch":"1","forge":false},
|
||||
{"rule":"x","branch":"dx","forge":false},
|
||||
{"rule":"]|)]","branch":"]|)]","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"[.*?","branch":"[.*?","forge":true},
|
||||
{"rule":"[?","branch":"[?","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"),/","branch":"a1,,1","forge":false},
|
||||
{"rule":"}+","branch":"}+","forge":true},
|
||||
{"rule":"$d\\,d","branch":"$d\\,d","forge":false},
|
||||
{"rule":"d1","branch":"d1","forge":true},
|
||||
{"rule":"-!^.:","branch":"-!^.:","forge":true},
|
||||
{"rule":"x","branch":"+].A11","forge":false},
|
||||
{"rule":"","branch":"-]","forge":false},
|
||||
{"rule":"}]}.","branch":"","forge":false},
|
||||
{"rule":"^{wx","branch":"]1a!","forge":false},
|
||||
{"rule":"^","branch":"*]x,","forge":false},
|
||||
{"rule":"(:x*x","branch":"","forge":false},
|
||||
{"rule":"|[/*a/","branch":"}","forge":false},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"1\\^","branch":"!é!x{","forge":false},
|
||||
{"rule":"(.b,AA+","branch":"(.b,AA+","forge":true},
|
||||
{"rule":"xx","branch":"","forge":false},
|
||||
{"rule":"d)^):","branch":"/}!","forge":false},
|
||||
{"rule":"|","branch":",é*]*[","forge":false},
|
||||
{"rule":"d*d:*1","branch":"\\b,","forge":false},
|
||||
{"rule":":]","branch":"[","forge":false},
|
||||
{"rule":"([|!\\^é","branch":"([|!\\^é","forge":true},
|
||||
{"rule":"$[w)x-*","branch":"","forge":false},
|
||||
{"rule":")","branch":")","forge":true},
|
||||
{"rule":".d^)?","branch":"x\\é","forge":false},
|
||||
{"rule":"1|{w","branch":"1|{w","forge":true},
|
||||
{"rule":"é$,(]$$","branch":"é$,(]$$","forge":true},
|
||||
{"rule":"/","branch":"/","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"+","branch":"","forge":false},
|
||||
{"rule":"]","branch":"","forge":false},
|
||||
{"rule":"+:a:","branch":"+:a:","forge":true},
|
||||
{"rule":"/*","branch":"d.}","forge":false},
|
||||
{"rule":"|/ba1-","branch":"|/ba1-","forge":true},
|
||||
{"rule":"","branch":"+","forge":false},
|
||||
{"rule":"A","branch":"A","forge":true},
|
||||
{"rule":",-!]","branch":",-!]","forge":true},
|
||||
{"rule":"$*d!)","branch":"","forge":false},
|
||||
{"rule":")é+x.d","branch":")é+x.d","forge":true},
|
||||
{"rule":"éa$\\[1","branch":"1/,","forge":false},
|
||||
{"rule":"}?1|]d","branch":"}?1|]d","forge":true},
|
||||
{"rule":"[","branch":"[","forge":true},
|
||||
{"rule":"-1.*+","branch":"-1.*+","forge":true},
|
||||
{"rule":"Aw|{","branch":"Aw|{","forge":true},
|
||||
{"rule":"adAAAa","branch":"A","forge":false},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"d/^{:","branch":"d/^{:","forge":true},
|
||||
{"rule":"é:(,^","branch":"é:(,^","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"!ébé({+","branch":"!ébé({+","forge":true},
|
||||
{"rule":"x","branch":"x","forge":true},
|
||||
{"rule":"|","branch":",:*","forge":false},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"x|","branch":"x|","forge":true},
|
||||
{"rule":"a","branch":"aaxéb,","forge":false},
|
||||
{"rule":"/w-),1é","branch":"/w-),1é","forge":true},
|
||||
{"rule":"|*)^,","branch":"|*)^,","forge":true},
|
||||
{"rule":"){é.!b.","branch":"a-}:","forge":false},
|
||||
{"rule":"(\\","branch":"(\\","forge":true},
|
||||
{"rule":".w(\\","branch":"+","forge":false},
|
||||
{"rule":"/d[","branch":"/d[","forge":true},
|
||||
{"rule":"(}$x!}","branch":"a,bb","forge":false},
|
||||
{"rule":"w{","branch":"w{","forge":true},
|
||||
{"rule":"[+a\\A(+","branch":"[+a\\A(+","forge":true},
|
||||
{"rule":"(1^{)bb","branch":"(1^{)bb","forge":true},
|
||||
{"rule":"^1","branch":"é-}","forge":false},
|
||||
{"rule":"*,+}1):","branch":"*,+}1):","forge":true},
|
||||
{"rule":"[A[(.x,","branch":"]db!*","forge":false},
|
||||
{"rule":"","branch":"Ad}","forge":false},
|
||||
{"rule":".","branch":".","forge":true},
|
||||
{"rule":"w!é{x","branch":"é.{","forge":false},
|
||||
{"rule":"$\\^","branch":"bx]+","forge":false},
|
||||
{"rule":"1)","branch":"+*d","forge":false},
|
||||
{"rule":"x)}/])","branch":"]","forge":false},
|
||||
{"rule":",a}.1}","branch":",a}.1}","forge":true},
|
||||
{"rule":"-a{(","branch":"-a{(","forge":true},
|
||||
{"rule":"1","branch":"1","forge":true},
|
||||
{"rule":"","branch":"","forge":true},
|
||||
{"rule":"}[é$","branch":"]d-!","forge":false},
|
||||
{"rule":"xé{é+?","branch":"xé{é+?","forge":true},
|
||||
{"rule":"/A1!","branch":"xAA\\::","forge":false},
|
||||
{"rule":"1][b-.(","branch":"{/-b![","forge":false},
|
||||
{"rule":"(d}(","branch":"(d}(","forge":true},
|
||||
{"rule":"{*}","branch":"!ébd","forge":true},
|
||||
{"rule":"()^^)","branch":"()^^)","forge":true},
|
||||
{"rule":"1(1A{$","branch":"*,[","forge":false},
|
||||
{"rule":"A:!^\\","branch":"A:!^\\","forge":true},
|
||||
{"rule":"-]:b]","branch":"-]:b]","forge":true},
|
||||
{"rule":",","branch":",","forge":true},
|
||||
{"rule":"[-:]","branch":"[-:]","forge":false},
|
||||
{"rule":"","branch":"é:b1:d","forge":false},
|
||||
{"rule":"1\\!]?d","branch":"","forge":false},
|
||||
{"rule":"","branch":"bb!]+","forge":false},
|
||||
{"rule":"/da","branch":"b]d[","forge":false},
|
||||
{"rule":",]","branch":",]","forge":true},
|
||||
{"rule":"[","branch":"[","forge":true},
|
||||
{"rule":"a]{A-","branch":"-+","forge":false},
|
||||
{"rule":"A?A^1","branch":"A?A^1","forge":true},
|
||||
{"rule":"\\-$","branch":"","forge":false},
|
||||
{"rule":"+","branch":"*[}[","forge":false},
|
||||
{"rule":",b?{dw","branch":",b?{dw","forge":true},
|
||||
{"rule":"d[$b1","branch":"/","forge":false},
|
||||
{"rule":"})|xAA","branch":"é,!","forge":false},
|
||||
{"rule":"]--","branch":"]--","forge":true},
|
||||
{"rule":"],/b","branch":"],/b","forge":true},
|
||||
{"rule":"/","branch":"/","forge":true},
|
||||
{"rule":"*aé","branch":"*aé","forge":true},
|
||||
{"rule":"{","branch":"{","forge":true},
|
||||
{"rule":":aé","branch":"","forge":false},
|
||||
{"rule":".\\}","branch":".\\}","forge":false},
|
||||
{"rule":"wA]*","branch":"\\]!d{+","forge":false},
|
||||
{"rule":"a/1x$/","branch":"[1!:/A","forge":false},
|
||||
{"rule":"[.wb}d.","branch":"-!*..a","forge":false},
|
||||
{"rule":".[{()a","branch":"+}","forge":false},
|
||||
{"rule":"]/!:,","branch":"{Aéa","forge":false},
|
||||
{"rule":"é{ba[-","branch":"],,1d/","forge":false},
|
||||
{"rule":"(w*","branch":"1}.!","forge":false},
|
||||
{"rule":"{?","branch":"1","forge":false},
|
||||
{"rule":")","branch":",+!","forge":false},
|
||||
{"rule":":","branch":"{/+A][","forge":false},
|
||||
{"rule":".1{)","branch":"a\\bA","forge":false},
|
||||
{"rule":"w-1+x1-","branch":"*]1}bb","forge":false},
|
||||
{"rule":"xb$$","branch":"éx:]-:","forge":false},
|
||||
{"rule":"++$!?*$","branch":"bA","forge":false},
|
||||
{"rule":"]{","branch":"[xd*x.","forge":false}
|
||||
]
|
||||
Reference in New Issue
Block a user