Skip to content

Commit 4ea6b91

Browse files
authored
Merge pull request #313 from TFNS/269-parser-logic-improvement
Parser logic improvements
2 parents 4045522 + 10d5afa commit 4ea6b91

File tree

10 files changed

+88
-103
lines changed

10 files changed

+88
-103
lines changed

front/src/components/Dialogs/TaskImportDialog.vue

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,29 @@
2626
<template #prepend>
2727
<q-icon name="data_object" />
2828
</template>
29+
<template #option="scope">
30+
<q-item v-bind="scope.itemProps">
31+
<q-item-section>
32+
<q-item-label class="flex">
33+
{{ scope.opt.label }}
34+
<q-space />
35+
{{
36+
scope.opt.amount > 0 ? `(${scope.opt.amount})` : '(0)'
37+
}}
38+
</q-item-label>
39+
</q-item-section>
40+
</q-item>
41+
</template>
42+
<template #selected-item="scope">
43+
<q-item-section>
44+
<q-item-label>
45+
{{ scope.opt.label }}
46+
{{
47+
scope.opt.amount > 0 ? `(${scope.opt.amount})` : '(0)'
48+
}}
49+
</q-item-label>
50+
</q-item-section>
51+
</template>
2952
</q-select>
3053

3154
<q-input
@@ -41,7 +64,10 @@
4164
'font-family': 'monospace',
4265
}"
4366
:hint="currentParser.value.hint"
44-
@paste="onPaste"
67+
@paste="detectParser"
68+
@blur="detectParser"
69+
@focus="detectParser"
70+
@update:model-value="detectParser"
4571
/>
4672
</div>
4773
</q-tab-panel>
@@ -95,6 +121,7 @@ import { Ctf, makeId } from 'src/ctfnote/models';
95121
import parsers, { ParsedTask } from 'src/ctfnote/parsers';
96122
import { defineComponent, ref } from 'vue';
97123
import TaskTagsList from 'src/components/Task/TaskTagsList.vue';
124+
import RawParser from 'src/ctfnote/parsers/raw';
98125
99126
export default defineComponent({
100127
components: {
@@ -107,7 +134,11 @@ export default defineComponent({
107134
setup() {
108135
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
109136
useDialogPluginComponent();
110-
const parserOptions = parsers.map((p) => ({ label: p.name, value: p }));
137+
const parserOptions = parsers.map((p) => ({
138+
label: p.name,
139+
value: p,
140+
amount: 0,
141+
}));
111142
112143
const columns = [
113144
{ name: 'keep', label: '', field: 'keep' },
@@ -165,14 +196,53 @@ export default defineComponent({
165196
}
166197
},
167198
autoDetectParser() {
168-
for (const parser of parsers) {
169-
if (parser.isValid(this.model)) {
170-
const p = this.parserOptions.find((opt) => opt.value == parser);
171-
if (p) this.currentParser = p;
199+
const outputOfParser = parsers.map((p) => {
200+
let challenges: ParsedTask[] = [];
201+
try {
202+
challenges = p.parse(this.model);
203+
} catch (e) {}
204+
205+
return {
206+
parser: p,
207+
challenges: challenges,
208+
};
209+
});
210+
211+
// assign the amount of challenges to the parser options
212+
this.parserOptions.forEach((opt) => {
213+
const parser = outputOfParser.find(
214+
(p) => p.parser.name === opt.value.name
215+
);
216+
if (parser) {
217+
opt.amount = parser.challenges.length;
172218
}
219+
});
220+
221+
// find the parser with the most tasks, but exclude the raw parser
222+
// since it will count the amount of newlines which does not always make sense
223+
const max = outputOfParser
224+
.filter((p) => p.parser.name !== RawParser.name)
225+
.reduce(
226+
(acc, cur) =>
227+
cur.challenges.length > acc ? cur.challenges.length : acc,
228+
0
229+
);
230+
const bestParser = outputOfParser.find((p) => p.challenges.length == max);
231+
if (
232+
bestParser &&
233+
bestParser.challenges.length > 0 &&
234+
(bestParser.challenges.length > this.currentParser.amount ||
235+
this.currentParser.label == RawParser.name) // it must be an improvement, except overriding the raw parser is allowed
236+
) {
237+
const p = this.parserOptions.find(
238+
(opt) => opt.value == bestParser.parser
239+
);
240+
if (p) this.currentParser = p;
241+
} else if (this.currentParser.amount == 0) {
242+
this.currentParser = this.parserOptions[0];
173243
}
174244
},
175-
onPaste() {
245+
detectParser() {
176246
void this.$nextTick(() => this.autoDetectParser());
177247
},
178248
normalizeTags(tags: string[]): string[] {

front/src/ctfnote/parsers/angstrom.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ParsedTask, Parser } from '.';
2-
import { parseJson, parseJsonStrict } from '../utils';
2+
import { parseJsonStrict } from '../utils';
33

44
const AngstromParser: Parser = {
55
name: 'ångstromCTF parser',
@@ -26,19 +26,6 @@ const AngstromParser: Parser = {
2626
}
2727
return tasks;
2828
},
29-
isValid(s) {
30-
const data =
31-
parseJson<
32-
Array<{ title: string; category: string; description: string }>
33-
>(s);
34-
return (
35-
Array.isArray(data) &&
36-
data.length > 0 &&
37-
data[0].title != null &&
38-
data[0].category != null &&
39-
data[0].description != null
40-
);
41-
},
4229
};
4330

4431
export default AngstromParser;

front/src/ctfnote/parsers/cini.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ParsedTask, Parser } from '.';
2-
import { parseJson, parseJsonStrict } from '../utils';
2+
import { parseJsonStrict } from '../utils';
33

44
interface Events {
55
gamePause?: unknown;
@@ -46,11 +46,6 @@ const CINIParser: Parser = {
4646

4747
return tasks;
4848
},
49-
isValid(s) {
50-
const data = parseJson<Events>(s);
51-
if (data == null) return false;
52-
return data.gamePause !== undefined && data.events !== undefined;
53-
},
5449
};
5550

5651
export default CINIParser;

front/src/ctfnote/parsers/ctfd.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ParsedTask, Parser } from '.';
2-
import { parseJson, parseJsonStrict } from '../utils';
2+
import { parseJsonStrict } from '../utils';
33

44
interface CTFdTags {
55
value: string;
@@ -30,10 +30,6 @@ const CTFDParser: Parser = {
3030
}
3131
return tasks;
3232
},
33-
isValid(s) {
34-
const data = parseJson<{ data?: unknown }>(s);
35-
return Array.isArray(data?.data);
36-
},
3733
};
3834

3935
export default CTFDParser;

front/src/ctfnote/parsers/hitcon.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,6 @@ const HitconParser: Parser = {
2828

2929
return tasks;
3030
},
31-
isValid(s) {
32-
const data =
33-
parseJsonStrict<
34-
[{ name: string; category: string; description: string }]
35-
>(s);
36-
if (data == null || data.length < 1) {
37-
return false;
38-
}
39-
return (
40-
data[0].name != null &&
41-
data[0].category != null &&
42-
data[0].description != null
43-
);
44-
},
4531
};
4632

4733
export default HitconParser;

front/src/ctfnote/parsers/htb.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ParsedTask, Parser } from '.';
2-
import { parseJson, parseJsonStrict } from '../utils';
2+
import { parseJsonStrict } from '../utils';
33

44
// output of https://ctf.hackthebox.com/api/public/challengeCategories
55
const challengeCategories: { [index: number]: string } = {
@@ -67,17 +67,6 @@ const HTBParser: Parser = {
6767
}
6868
return tasks;
6969
},
70-
isValid(s) {
71-
const data = parseJson<{
72-
challenges: Array<{
73-
id: number;
74-
name: string;
75-
description: string;
76-
challenge_category_id: number;
77-
}>;
78-
}>(s);
79-
return Array.isArray(data?.challenges);
80-
},
8170
};
8271

8372
export default HTBParser;

front/src/ctfnote/parsers/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ export type ParsedTask = {
1717
export type Parser = {
1818
name: string;
1919
hint: string;
20-
isValid(s: string): boolean;
2120
parse(s: string): ParsedTask[];
2221
};
2322

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ParsedTask, Parser } from '.';
2-
import { parseJson, parseJsonStrict } from '../utils';
2+
import { parseJsonStrict } from '../utils';
33

44
const justCTFParser: Parser = {
55
name: 'justCTF parser',
@@ -25,7 +25,11 @@ const justCTFParser: Parser = {
2525
}
2626

2727
for (const challenge of data) {
28-
if (!challenge.description || !challenge.name) {
28+
if (
29+
!challenge.description ||
30+
!challenge.name ||
31+
!Array.isArray(challenge.categories)
32+
) {
2933
continue;
3034
}
3135

@@ -37,29 +41,6 @@ const justCTFParser: Parser = {
3741
}
3842
return tasks;
3943
},
40-
isValid(s) {
41-
const data = parseJson<
42-
[
43-
{
44-
id: number;
45-
name: string;
46-
categories: [string];
47-
difficult: string;
48-
description: string;
49-
points: number;
50-
solvers: number;
51-
}
52-
]
53-
>(s);
54-
return (
55-
data != null &&
56-
data?.length > 0 &&
57-
data[0].id != null &&
58-
data[0].name != null &&
59-
Array.isArray(data[0].categories) &&
60-
data[0].points != null
61-
);
62-
},
6344
};
6445

6546
export default justCTFParser;

front/src/ctfnote/parsers/pico.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ParsedTask, Parser } from '.';
2-
import { parseJson, parseJsonStrict } from '../utils';
2+
import { parseJsonStrict } from '../utils';
33

44
const PicoParser: Parser = {
55
name: 'picoCTF parser',
@@ -21,18 +21,6 @@ const PicoParser: Parser = {
2121
}
2222
return tasks;
2323
},
24-
isValid(s) {
25-
const data = parseJson<{
26-
results: Array<{ name: string; category: { name: string } }>;
27-
}>(s);
28-
return (
29-
data?.results != null &&
30-
Array.isArray(data?.results) &&
31-
data?.results.length > 0 &&
32-
data?.results[0].name != null &&
33-
data?.results[0].category.name != null
34-
);
35-
},
3624
};
3725

3826
export default PicoParser;

front/src/ctfnote/parsers/raw.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ const RawParser: Parser = {
1919
}
2020
return tasks;
2121
},
22-
isValid(s) {
23-
return s
24-
.trim()
25-
.split('\n')
26-
.every((s) => /[^|]+\|[^|]+/.exec(s));
27-
},
2822
};
2923

3024
export default RawParser;

0 commit comments

Comments
 (0)