diff --git a/front/src/components/Dialogs/TaskImportDialog.vue b/front/src/components/Dialogs/TaskImportDialog.vue
index 5018d52b..21277ac4 100644
--- a/front/src/components/Dialogs/TaskImportDialog.vue
+++ b/front/src/components/Dialogs/TaskImportDialog.vue
@@ -26,6 +26,29 @@
+
+
+
+
+ {{ scope.opt.label }}
+
+ {{
+ scope.opt.amount > 0 ? `(${scope.opt.amount})` : '(0)'
+ }}
+
+
+
+
+
+
+
+ {{ scope.opt.label }}
+ {{
+ scope.opt.amount > 0 ? `(${scope.opt.amount})` : '(0)'
+ }}
+
+
+
@@ -95,6 +121,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 +134,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 +196,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[] {
diff --git a/front/src/ctfnote/parsers/angstrom.ts b/front/src/ctfnote/parsers/angstrom.ts
index d3158057..551cb0e6 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',
@@ -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 d1d9c311..07c8f246 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;
@@ -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 6048cff6..20ceb418 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;
@@ -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 46491601..20c75ff6 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 cb854dd4..4e7139c1 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 } = {
@@ -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 ca4c1360..add3bdce 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 1d076a29..e5297bdd 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',
@@ -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;
}
@@ -37,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 62e90a0b..dc9536be 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',
@@ -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 22544f6a..e57a3093 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;