From 8505905d7f8e74d3e0836b936da7d1cf718b4155 Mon Sep 17 00:00:00 2001
From: JJ-8 <34778827+JJ-8@users.noreply.github.com>
Date: Sat, 10 Aug 2024 21:44:08 +0200
Subject: [PATCH 1/5] Parse challenges by `parser` instead of detection through
`isValid`
Now we just try to parse the input with each parser, ignoring errors. Then we select the parser with the most parsed challenges,
except for some conditions with the raw parser.
The raw parser will see every newline as a new task and when pasting a beautified JSON object,
it should not be the best parser when other parsers also detect challenges.
---
.../components/Dialogs/TaskImportDialog.vue | 83 +++++++++++++++++--
1 file changed, 76 insertions(+), 7 deletions(-)
diff --git a/front/src/components/Dialogs/TaskImportDialog.vue b/front/src/components/Dialogs/TaskImportDialog.vue
index 5018d52ba..ed4ee3c95 100644
--- a/front/src/components/Dialogs/TaskImportDialog.vue
+++ b/front/src/components/Dialogs/TaskImportDialog.vue
@@ -26,6 +26,28 @@
+
+
+
+
+ {{ scope.opt.label }}
+ {{
+ scope.opt.amount > 0 ? `(${scope.opt.amount})` : '(0)'
+ }}
+
+
+
+
+
+
+
+ {{ scope.opt.label }}
+ {{
+ scope.opt.amount > 0 ? `(${scope.opt.amount})` : '(0)'
+ }}
+
+
+
@@ -95,6 +120,7 @@ import { Ctf, makeId } from 'src/ctfnote/models';
import parsers, { ParsedTask } from 'src/ctfnote/parsers';
import { defineComponent, ref } from 'vue';
import TaskTagsList from 'src/components/Task/TaskTagsList.vue';
+import RawParser from 'src/ctfnote/parsers/raw';
export default defineComponent({
components: {
@@ -107,7 +133,11 @@ export default defineComponent({
setup() {
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
useDialogPluginComponent();
- const parserOptions = parsers.map((p) => ({ label: p.name, value: p }));
+ const parserOptions = parsers.map((p) => ({
+ label: p.name,
+ value: p,
+ amount: 0,
+ }));
const columns = [
{ name: 'keep', label: '', field: 'keep' },
@@ -165,14 +195,53 @@ export default defineComponent({
}
},
autoDetectParser() {
- for (const parser of parsers) {
- if (parser.isValid(this.model)) {
- const p = this.parserOptions.find((opt) => opt.value == parser);
- if (p) this.currentParser = p;
+ const outputOfParser = parsers.map((p) => {
+ let challenges: ParsedTask[] = [];
+ try {
+ challenges = p.parse(this.model);
+ } catch (e) {}
+
+ return {
+ parser: p,
+ challenges: challenges,
+ };
+ });
+
+ // assign the amount of challenges to the parser options
+ this.parserOptions.forEach((opt) => {
+ const parser = outputOfParser.find(
+ (p) => p.parser.name === opt.value.name
+ );
+ if (parser) {
+ opt.amount = parser.challenges.length;
}
+ });
+
+ // find the parser with the most tasks, but exclude the raw parser
+ // since it will count the amount of newlines which does not always make sense
+ const max = outputOfParser
+ .filter((p) => p.parser.name !== RawParser.name)
+ .reduce(
+ (acc, cur) =>
+ cur.challenges.length > acc ? cur.challenges.length : acc,
+ 0
+ );
+ const bestParser = outputOfParser.find((p) => p.challenges.length == max);
+ if (
+ bestParser &&
+ bestParser.challenges.length > 0 &&
+ (bestParser.challenges.length > this.currentParser.amount ||
+ this.currentParser.label == RawParser.name) // it must be an improvement, except overriding the raw parser is allowed
+ ) {
+ const p = this.parserOptions.find(
+ (opt) => opt.value == bestParser.parser
+ );
+ if (p) this.currentParser = p;
+ } else if (this.currentParser.amount == 0) {
+ this.currentParser = this.parserOptions[0];
}
},
- onPaste() {
+ detectParser() {
void this.$nextTick(() => this.autoDetectParser());
},
normalizeTags(tags: string[]): string[] {
From 43565128ed19382dbd0c0418252891d45469bffd Mon Sep 17 00:00:00 2001
From: JJ-8 <34778827+JJ-8@users.noreply.github.com>
Date: Sat, 10 Aug 2024 21:44:48 +0200
Subject: [PATCH 2/5] Fix justCTF tag parsing
An array is expected for the categories of justCTF tasks.
---
front/src/ctfnote/parsers/justctf.ts | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/front/src/ctfnote/parsers/justctf.ts b/front/src/ctfnote/parsers/justctf.ts
index 1d076a29d..c896d479b 100644
--- a/front/src/ctfnote/parsers/justctf.ts
+++ b/front/src/ctfnote/parsers/justctf.ts
@@ -25,7 +25,11 @@ const justCTFParser: Parser = {
}
for (const challenge of data) {
- if (!challenge.description || !challenge.name) {
+ if (
+ !challenge.description ||
+ !challenge.name ||
+ !Array.isArray(challenge.categories)
+ ) {
continue;
}
From 3b4c7223c7791faadd9ba25b6689839d4fb8914b Mon Sep 17 00:00:00 2001
From: JJ-8 <34778827+JJ-8@users.noreply.github.com>
Date: Sat, 10 Aug 2024 21:47:15 +0200
Subject: [PATCH 3/5] Drop `isValid` functions of parsers
Not in use anymore so we can safely drop it.
---
front/src/ctfnote/parsers/angstrom.ts | 13 -------------
front/src/ctfnote/parsers/cini.ts | 5 -----
front/src/ctfnote/parsers/ctfd.ts | 4 ----
front/src/ctfnote/parsers/hitcon.ts | 14 --------------
front/src/ctfnote/parsers/htb.ts | 11 -----------
front/src/ctfnote/parsers/index.ts | 1 -
front/src/ctfnote/parsers/justctf.ts | 23 -----------------------
front/src/ctfnote/parsers/pico.ts | 12 ------------
front/src/ctfnote/parsers/raw.ts | 6 ------
9 files changed, 89 deletions(-)
diff --git a/front/src/ctfnote/parsers/angstrom.ts b/front/src/ctfnote/parsers/angstrom.ts
index d31580575..93d2bbb99 100644
--- a/front/src/ctfnote/parsers/angstrom.ts
+++ b/front/src/ctfnote/parsers/angstrom.ts
@@ -26,19 +26,6 @@ const AngstromParser: Parser = {
}
return tasks;
},
- isValid(s) {
- const data =
- parseJson<
- Array<{ title: string; category: string; description: string }>
- >(s);
- return (
- Array.isArray(data) &&
- data.length > 0 &&
- data[0].title != null &&
- data[0].category != null &&
- data[0].description != null
- );
- },
};
export default AngstromParser;
diff --git a/front/src/ctfnote/parsers/cini.ts b/front/src/ctfnote/parsers/cini.ts
index d1d9c311f..f06e41b3e 100644
--- a/front/src/ctfnote/parsers/cini.ts
+++ b/front/src/ctfnote/parsers/cini.ts
@@ -46,11 +46,6 @@ const CINIParser: Parser = {
return tasks;
},
- isValid(s) {
- const data = parseJson(s);
- if (data == null) return false;
- return data.gamePause !== undefined && data.events !== undefined;
- },
};
export default CINIParser;
diff --git a/front/src/ctfnote/parsers/ctfd.ts b/front/src/ctfnote/parsers/ctfd.ts
index 6048cff60..80847726f 100644
--- a/front/src/ctfnote/parsers/ctfd.ts
+++ b/front/src/ctfnote/parsers/ctfd.ts
@@ -30,10 +30,6 @@ const CTFDParser: Parser = {
}
return tasks;
},
- isValid(s) {
- const data = parseJson<{ data?: unknown }>(s);
- return Array.isArray(data?.data);
- },
};
export default CTFDParser;
diff --git a/front/src/ctfnote/parsers/hitcon.ts b/front/src/ctfnote/parsers/hitcon.ts
index 464916013..20c75ff68 100644
--- a/front/src/ctfnote/parsers/hitcon.ts
+++ b/front/src/ctfnote/parsers/hitcon.ts
@@ -28,20 +28,6 @@ const HitconParser: Parser = {
return tasks;
},
- isValid(s) {
- const data =
- parseJsonStrict<
- [{ name: string; category: string; description: string }]
- >(s);
- if (data == null || data.length < 1) {
- return false;
- }
- return (
- data[0].name != null &&
- data[0].category != null &&
- data[0].description != null
- );
- },
};
export default HitconParser;
diff --git a/front/src/ctfnote/parsers/htb.ts b/front/src/ctfnote/parsers/htb.ts
index cb854dd44..621a93b35 100644
--- a/front/src/ctfnote/parsers/htb.ts
+++ b/front/src/ctfnote/parsers/htb.ts
@@ -67,17 +67,6 @@ const HTBParser: Parser = {
}
return tasks;
},
- isValid(s) {
- const data = parseJson<{
- challenges: Array<{
- id: number;
- name: string;
- description: string;
- challenge_category_id: number;
- }>;
- }>(s);
- return Array.isArray(data?.challenges);
- },
};
export default HTBParser;
diff --git a/front/src/ctfnote/parsers/index.ts b/front/src/ctfnote/parsers/index.ts
index ca4c13606..add3bdce4 100644
--- a/front/src/ctfnote/parsers/index.ts
+++ b/front/src/ctfnote/parsers/index.ts
@@ -17,7 +17,6 @@ export type ParsedTask = {
export type Parser = {
name: string;
hint: string;
- isValid(s: string): boolean;
parse(s: string): ParsedTask[];
};
diff --git a/front/src/ctfnote/parsers/justctf.ts b/front/src/ctfnote/parsers/justctf.ts
index c896d479b..be5e3bf0e 100644
--- a/front/src/ctfnote/parsers/justctf.ts
+++ b/front/src/ctfnote/parsers/justctf.ts
@@ -41,29 +41,6 @@ const justCTFParser: Parser = {
}
return tasks;
},
- isValid(s) {
- const data = parseJson<
- [
- {
- id: number;
- name: string;
- categories: [string];
- difficult: string;
- description: string;
- points: number;
- solvers: number;
- }
- ]
- >(s);
- return (
- data != null &&
- data?.length > 0 &&
- data[0].id != null &&
- data[0].name != null &&
- Array.isArray(data[0].categories) &&
- data[0].points != null
- );
- },
};
export default justCTFParser;
diff --git a/front/src/ctfnote/parsers/pico.ts b/front/src/ctfnote/parsers/pico.ts
index 62e90a0be..a1193f9c5 100755
--- a/front/src/ctfnote/parsers/pico.ts
+++ b/front/src/ctfnote/parsers/pico.ts
@@ -21,18 +21,6 @@ const PicoParser: Parser = {
}
return tasks;
},
- isValid(s) {
- const data = parseJson<{
- results: Array<{ name: string; category: { name: string } }>;
- }>(s);
- return (
- data?.results != null &&
- Array.isArray(data?.results) &&
- data?.results.length > 0 &&
- data?.results[0].name != null &&
- data?.results[0].category.name != null
- );
- },
};
export default PicoParser;
diff --git a/front/src/ctfnote/parsers/raw.ts b/front/src/ctfnote/parsers/raw.ts
index 22544f6a8..e57a3093c 100644
--- a/front/src/ctfnote/parsers/raw.ts
+++ b/front/src/ctfnote/parsers/raw.ts
@@ -19,12 +19,6 @@ const RawParser: Parser = {
}
return tasks;
},
- isValid(s) {
- return s
- .trim()
- .split('\n')
- .every((s) => /[^|]+\|[^|]+/.exec(s));
- },
};
export default RawParser;
From 2dd20df62a958dd080b88849bdafa2109ead6e0e Mon Sep 17 00:00:00 2001
From: JJ-8 <34778827+JJ-8@users.noreply.github.com>
Date: Sat, 10 Aug 2024 22:10:53 +0200
Subject: [PATCH 4/5] Remove unused imports
Due to dropping the `isValid` function, these imports are not used anymore.
---
front/src/ctfnote/parsers/angstrom.ts | 2 +-
front/src/ctfnote/parsers/cini.ts | 2 +-
front/src/ctfnote/parsers/ctfd.ts | 2 +-
front/src/ctfnote/parsers/htb.ts | 2 +-
front/src/ctfnote/parsers/justctf.ts | 2 +-
front/src/ctfnote/parsers/pico.ts | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/front/src/ctfnote/parsers/angstrom.ts b/front/src/ctfnote/parsers/angstrom.ts
index 93d2bbb99..551cb0e67 100644
--- a/front/src/ctfnote/parsers/angstrom.ts
+++ b/front/src/ctfnote/parsers/angstrom.ts
@@ -1,5 +1,5 @@
import { ParsedTask, Parser } from '.';
-import { parseJson, parseJsonStrict } from '../utils';
+import { parseJsonStrict } from '../utils';
const AngstromParser: Parser = {
name: 'ångstromCTF parser',
diff --git a/front/src/ctfnote/parsers/cini.ts b/front/src/ctfnote/parsers/cini.ts
index f06e41b3e..07c8f2466 100644
--- a/front/src/ctfnote/parsers/cini.ts
+++ b/front/src/ctfnote/parsers/cini.ts
@@ -1,5 +1,5 @@
import { ParsedTask, Parser } from '.';
-import { parseJson, parseJsonStrict } from '../utils';
+import { parseJsonStrict } from '../utils';
interface Events {
gamePause?: unknown;
diff --git a/front/src/ctfnote/parsers/ctfd.ts b/front/src/ctfnote/parsers/ctfd.ts
index 80847726f..20ceb4185 100644
--- a/front/src/ctfnote/parsers/ctfd.ts
+++ b/front/src/ctfnote/parsers/ctfd.ts
@@ -1,5 +1,5 @@
import { ParsedTask, Parser } from '.';
-import { parseJson, parseJsonStrict } from '../utils';
+import { parseJsonStrict } from '../utils';
interface CTFdTags {
value: string;
diff --git a/front/src/ctfnote/parsers/htb.ts b/front/src/ctfnote/parsers/htb.ts
index 621a93b35..4e7139c10 100644
--- a/front/src/ctfnote/parsers/htb.ts
+++ b/front/src/ctfnote/parsers/htb.ts
@@ -1,5 +1,5 @@
import { ParsedTask, Parser } from '.';
-import { parseJson, parseJsonStrict } from '../utils';
+import { parseJsonStrict } from '../utils';
// output of https://ctf.hackthebox.com/api/public/challengeCategories
const challengeCategories: { [index: number]: string } = {
diff --git a/front/src/ctfnote/parsers/justctf.ts b/front/src/ctfnote/parsers/justctf.ts
index be5e3bf0e..e5297bdd9 100644
--- a/front/src/ctfnote/parsers/justctf.ts
+++ b/front/src/ctfnote/parsers/justctf.ts
@@ -1,5 +1,5 @@
import { ParsedTask, Parser } from '.';
-import { parseJson, parseJsonStrict } from '../utils';
+import { parseJsonStrict } from '../utils';
const justCTFParser: Parser = {
name: 'justCTF parser',
diff --git a/front/src/ctfnote/parsers/pico.ts b/front/src/ctfnote/parsers/pico.ts
index a1193f9c5..dc9536beb 100755
--- a/front/src/ctfnote/parsers/pico.ts
+++ b/front/src/ctfnote/parsers/pico.ts
@@ -1,5 +1,5 @@
import { ParsedTask, Parser } from '.';
-import { parseJson, parseJsonStrict } from '../utils';
+import { parseJsonStrict } from '../utils';
const PicoParser: Parser = {
name: 'picoCTF parser',
From 10d5afa52a1cb912e7c6108d83c1b383dfbba025 Mon Sep 17 00:00:00 2001
From: JJ-8 <34778827+JJ-8@users.noreply.github.com>
Date: Fri, 23 Aug 2024 13:57:07 +0200
Subject: [PATCH 5/5] Show amount of parsed tasks at the right of the dropdown
Processed feedback from B-i-t-K
(https://github.com/TFNS/CTFNote/pull/313#issuecomment-2306899789)
---
front/src/components/Dialogs/TaskImportDialog.vue | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/front/src/components/Dialogs/TaskImportDialog.vue b/front/src/components/Dialogs/TaskImportDialog.vue
index ed4ee3c95..21277ac4f 100644
--- a/front/src/components/Dialogs/TaskImportDialog.vue
+++ b/front/src/components/Dialogs/TaskImportDialog.vue
@@ -29,8 +29,9 @@
-
+
{{ scope.opt.label }}
+
{{
scope.opt.amount > 0 ? `(${scope.opt.amount})` : '(0)'
}}