From 5a79343af888b21842e0f4adcb3fb289dfc0ad67 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 21 Jan 2022 08:28:39 +0100 Subject: [PATCH] Implement sort_regexp_validator_groups --- admin/api/exercice.go | 23 +++++++++++----------- admin/sync/exercice_defines.go | 1 + admin/sync/exercice_keys.go | 3 ++- libfic/db.go | 1 + libfic/file.go | 2 +- libfic/flag_key.go | 36 ++++++++++++++++++++++------------ libfic/team_my.go | 2 +- 7 files changed, 42 insertions(+), 26 deletions(-) diff --git a/admin/api/exercice.go b/admin/api/exercice.go index 7015a429..c53dc931 100644 --- a/admin/api/exercice.go +++ b/admin/api/exercice.go @@ -399,16 +399,17 @@ func deleteExerciceHint(hint *fic.EHint, _ []byte) (interface{}, error) { } type uploadedFlag struct { - Type string - Label string - Placeholder string - IgnoreCase bool - Multiline bool - NoTrim bool - ValidatorRe *string `json:"validator_regexp"` - Flag string - Value []byte - ChoicesCost int64 `json:"choices_cost"` + Type string + Label string + Placeholder string + IgnoreCase bool + Multiline bool + NoTrim bool + ValidatorRe *string `json:"validator_regexp"` + SortReGroups bool `json:"sort_re_grps"` + Flag string + Value []byte + ChoicesCost int64 `json:"choices_cost"` } func createExerciceFlag(exercice *fic.Exercice, body []byte) (interface{}, error) { @@ -426,7 +427,7 @@ func createExerciceFlag(exercice *fic.Exercice, body []byte) (interface{}, error vre = uk.ValidatorRe } - return exercice.AddRawFlagKey(uk.Label, uk.Type, uk.Placeholder, uk.IgnoreCase, uk.NoTrim, uk.Multiline, vre, []byte(uk.Flag), uk.ChoicesCost) + return exercice.AddRawFlagKey(uk.Label, uk.Type, uk.Placeholder, uk.IgnoreCase, uk.NoTrim, uk.Multiline, vre, uk.SortReGroups, []byte(uk.Flag), uk.ChoicesCost) } func showExerciceFlag(flag *fic.FlagKey, _ *fic.Exercice, body []byte) (interface{}, error) { diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index e70c6506..f7841e33 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -40,6 +40,7 @@ type ExerciceFlag struct { CaseSensitive bool `toml:",omitempty"` NoTrim bool `toml:",omitempty"` ValidatorRe string `toml:"validator_regexp,omitempty"` + SortReGroups bool `toml:"sort_validator_regexp_groups,omitempty"` Placeholder string `toml:",omitempty"` Help string `toml:",omitempty"` ChoicesCost int64 `toml:"choices_cost,omitempty"` diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 40a87dbc..fc722555 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -124,7 +124,7 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau errs = append(errs, fmt.Sprintf("%q: WARNING flag #%d: non-printable characters in flag, is this really expected?", path.Base(exercice.Path), flagline)) } - hashedFlag, err := fic.ComputeHashedFlag([]byte(raw), !flag.CaseSensitive, flag.NoTrim, validatorRegexp(flag.ValidatorRe)) + hashedFlag, err := fic.ComputeHashedFlag([]byte(raw), !flag.CaseSensitive, flag.NoTrim, validatorRegexp(flag.ValidatorRe), flag.SortReGroups) if err != nil { errs = append(errs, fmt.Sprintf("%q: flag #%d: %s", path.Base(exercice.Path), flagline, err.Error())) return @@ -140,6 +140,7 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau IgnoreCase: !flag.CaseSensitive, Multiline: flag.Type == "text", ValidatorRegexp: validatorRegexp(flag.ValidatorRe), + SortReGroups: flag.SortReGroups, Checksum: hashedFlag[:], ChoicesCost: flag.ChoicesCost, }) diff --git a/libfic/db.go b/libfic/db.go index 634166ae..030a3446 100644 --- a/libfic/db.go +++ b/libfic/db.go @@ -193,6 +193,7 @@ CREATE TABLE IF NOT EXISTS exercice_flags( notrim BOOLEAN NOT NULL DEFAULT 0, multiline BOOLEAN NOT NULL DEFAULT 0, validator_regexp VARCHAR(255) NULL, + sort_re_grps BOOLEAN NOT NULL DEFAULT 0, cksum BINARY(64) NOT NULL, choices_cost INTEGER NOT NULL, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice) diff --git a/libfic/file.go b/libfic/file.go index b3a24717..ff9c83de 100644 --- a/libfic/file.go +++ b/libfic/file.go @@ -332,7 +332,7 @@ func (f *EFile) GetDepends() ([]Flag, error) { if err := rows.Scan(&d); err != nil { return nil, err } - deps = append(deps, &FlagKey{d, f.IdExercice, 0, "", "", "", "", "", false, false, false, nil, []byte{}, 0}) + deps = append(deps, &FlagKey{d, f.IdExercice, 0, "", "", "", "", "", false, false, false, nil, false, []byte{}, 0}) } if err := rows.Err(); err != nil { return nil, err diff --git a/libfic/flag_key.go b/libfic/flag_key.go index ffed3213..1ba98246 100644 --- a/libfic/flag_key.go +++ b/libfic/flag_key.go @@ -5,6 +5,8 @@ import ( "errors" "fmt" "regexp" + "sort" + "strings" "time" "golang.org/x/crypto/blake2b" @@ -35,6 +37,8 @@ type FlagKey struct { Multiline bool `json:"multiline"` // ValidatorRegexp extracts a subset of the player's answer, that will be checked. ValidatorRegexp *string `json:"validator_regexp"` + // SortReGroups indicates if groups resulting of the validator regexp should be sorted. + SortReGroups bool `json:"sort_re_grps"` // Checksum is the expected hashed flag Checksum []byte `json:"value"` // ChoicesCost is the number of points lost to display choices. @@ -43,7 +47,7 @@ type FlagKey struct { // GetFlagKeys returns a list of key's flags comming with the challenge. func (e *Exercice) GetFlagKeys() ([]*FlagKey, error) { - if rows, err := DBQuery("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE id_exercice = ?", e.Id); err != nil { + if rows, err := DBQuery("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost FROM exercice_flags WHERE id_exercice = ?", e.Id); err != nil { return nil, err } else { defer rows.Close() @@ -53,7 +57,7 @@ func (e *Exercice) GetFlagKeys() ([]*FlagKey, error) { k := &FlagKey{} k.IdExercice = e.Id - if err := rows.Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.NoTrim, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost); err != nil { + if err := rows.Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.NoTrim, &k.Multiline, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost); err != nil { return nil, err } @@ -70,19 +74,19 @@ func (e *Exercice) GetFlagKeys() ([]*FlagKey, error) { // GetFlagKey returns a list of flags comming with the challenge. func GetFlagKey(id int) (k *FlagKey, err error) { k = &FlagKey{} - err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE id_flag = ?", id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.Multiline, &k.NoTrim, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost) + err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost FROM exercice_flags WHERE id_flag = ?", id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.Multiline, &k.NoTrim, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost) return } // GetFlagKeyByLabel returns a flag matching the given label. func (e *Exercice) GetFlagKeyByLabel(label string) (k *FlagKey, err error) { k = &FlagKey{} - err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?", label, e.Id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.NoTrim, &k.Multiline, &k.ValidatorRegexp, &k.Checksum, &k.ChoicesCost) + err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?", label, e.Id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.NoTrim, &k.Multiline, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost) return } // ComputeHashedFlag calculates the expected checksum for the given raw_value. -func ComputeHashedFlag(raw_value []byte, ignorecase bool, notrim bool, validator_regexp *string) (hash [blake2b.Size]byte, err error) { +func ComputeHashedFlag(raw_value []byte, ignorecase bool, notrim bool, validator_regexp *string, sortregroups bool) (hash [blake2b.Size]byte, err error) { if ignorecase { raw_value = bytes.ToLower(raw_value) } @@ -93,7 +97,7 @@ func ComputeHashedFlag(raw_value []byte, ignorecase bool, notrim bool, validator // Check that raw value passes through the regexp if validator_regexp != nil { - if raw_value, err = ExecValidatorRegexp(*validator_regexp, raw_value, ignorecase); err != nil { + if raw_value, err = ExecValidatorRegexp(*validator_regexp, raw_value, ignorecase, sortregroups); err != nil { return } } @@ -107,7 +111,7 @@ func ComputeHashedFlag(raw_value []byte, ignorecase bool, notrim bool, validator return } -func ExecValidatorRegexp(vre string, val []byte, ignorecase bool) ([]byte, error) { +func ExecValidatorRegexp(vre string, val []byte, ignorecase bool, sortregroups bool) ([]byte, error) { if ignorecase { vre = "(?i)" + vre } @@ -115,14 +119,21 @@ func ExecValidatorRegexp(vre string, val []byte, ignorecase bool) ([]byte, error return val, err } else if res := re.FindSubmatch(val); res == nil { return val, errors.New("expected flag doesn't pass through the validator_regexp") + } else if sortregroups && len(res) > 2 { + var tab []string + for _, v := range res[1:] { + tab = append(tab, string(v)) + } + sort.Strings(tab) + return []byte(strings.Join(tab, "+")), nil } else { return bytes.Join(res[1:], []byte("+")), nil } } // AddRawFlagKey creates and fills a new struct FlagKey, from a non-hashed flag, and registers it into the database. -func (e *Exercice) AddRawFlagKey(name string, t string, placeholder string, ignorecase bool, multiline bool, notrim bool, validator_regexp *string, raw_value []byte, choicescost int64) (*FlagKey, error) { - hash, err := ComputeHashedFlag(raw_value, ignorecase, notrim, validator_regexp) +func (e *Exercice) AddRawFlagKey(name string, t string, placeholder string, ignorecase bool, multiline bool, notrim bool, validator_regexp *string, sortregroups bool, raw_value []byte, choicescost int64) (*FlagKey, error) { + hash, err := ComputeHashedFlag(raw_value, ignorecase, notrim, validator_regexp, sortregroups) if err != nil { return nil, err } @@ -135,6 +146,7 @@ func (e *Exercice) AddRawFlagKey(name string, t string, placeholder string, igno NoTrim: notrim, Multiline: multiline, ValidatorRegexp: validator_regexp, + SortReGroups: sortregroups, Checksum: hash[:], ChoicesCost: choicescost, } @@ -166,7 +178,7 @@ func (k *FlagKey) Create(e *Exercice) (Flag, error) { } } - if res, err := DBExec("INSERT INTO exercice_flags (id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, cksum, choices_cost) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", e.Id, k.Order, k.Label, k.Type, k.Placeholder, k.Help, k.Unit, k.IgnoreCase, k.NoTrim, k.Multiline, k.ValidatorRegexp, k.Checksum, k.ChoicesCost); err != nil { + if res, err := DBExec("INSERT INTO exercice_flags (id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", e.Id, k.Order, k.Label, k.Type, k.Placeholder, k.Help, k.Unit, k.IgnoreCase, k.NoTrim, k.Multiline, k.ValidatorRegexp, k.SortReGroups, k.Checksum, k.ChoicesCost); err != nil { return k, err } else if kid, err := res.LastInsertId(); err != nil { return k, err @@ -179,7 +191,7 @@ func (k *FlagKey) Create(e *Exercice) (Flag, error) { // ComputeChecksum calculates the checksum for a given value. func (k *FlagKey) ComputeChecksum(val []byte) ([]byte, error) { - cksum, err := ComputeHashedFlag(val, k.IgnoreCase, k.NoTrim, k.ValidatorRegexp) + cksum, err := ComputeHashedFlag(val, k.IgnoreCase, k.NoTrim, k.ValidatorRegexp, k.SortReGroups) return cksum[:], err } @@ -191,7 +203,7 @@ func (k *FlagKey) Update() (int64, error) { } } - if res, err := DBExec("UPDATE exercice_flags SET id_exercice = ?, ordre = ?, label = ?, type = ?, placeholder = ?, help = ?, unit = ?, ignorecase = ?, notrim = ?, multiline = ?, validator_regexp = ?, cksum = ?, choices_cost = ? WHERE id_flag = ?", k.IdExercice, k.Order, k.Label, k.Type, k.Placeholder, k.Help, k.Unit, k.IgnoreCase, k.NoTrim, k.Multiline, k.ValidatorRegexp, k.Checksum, k.ChoicesCost, k.Id); err != nil { + if res, err := DBExec("UPDATE exercice_flags SET id_exercice = ?, ordre = ?, label = ?, type = ?, placeholder = ?, help = ?, unit = ?, ignorecase = ?, notrim = ?, multiline = ?, validator_regexp = ?, sort_re_grps = ?, cksum = ?, choices_cost = ? WHERE id_flag = ?", k.IdExercice, k.Order, k.Label, k.Type, k.Placeholder, k.Help, k.Unit, k.IgnoreCase, k.NoTrim, k.Multiline, k.ValidatorRegexp, k.SortReGroups, k.Checksum, k.ChoicesCost, k.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err diff --git a/libfic/team_my.go b/libfic/team_my.go index fda6faa5..f860e021 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -356,7 +356,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { } if t == nil { - h, _ := ComputeHashedFlag([]byte(soluce), false, false, nil) + h, _ := ComputeHashedFlag([]byte(soluce), false, false, nil, false) m.Soluce = hex.EncodeToString(h[:]) }