Compare commits

..

65 commits

Author SHA1 Message Date
821efbe41c chore(deps): update dependency svelte to v5
Some checks failed
continuous-integration/drone/push Build is failing
2025-03-28 14:07:37 +00:00
cb0e0e2c24 chore(deps): update module golang.org/x/oauth2 to v0.28.0
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-03-28 10:07:14 +00:00
6100f33e7c chore(deps): update module golang.org/x/image to v0.25.0
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-28 08:26:08 +00:00
47f2004a4c chore(deps): update dependency eslint-plugin-svelte to v3
Some checks are pending
continuous-integration/drone/push Build is running
2025-03-28 08:25:55 +00:00
faf74ec808 chore(deps): update module github.com/go-git/go-git/v5 to v5.14.0
Some checks are pending
continuous-integration/drone/push Build is running
2025-03-27 22:22:56 +00:00
e42545416f chore(deps): update module golang.org/x/crypto to v0.36.0
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-03-27 16:08:13 +00:00
3f5d6bb04b chore(deps): update module github.com/burntsushi/toml to v1.5.0
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-03-27 12:06:30 +00:00
99c436ba9a Keep repochecker on 3.19 (needed for grammalecte)
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-27 12:28:48 +01:00
a7d521fbdd admin: Can change capture_regexp along with key 2025-03-27 12:28:16 +01:00
0c53372618 admin: Implement .gz file download test 2025-03-27 12:28:16 +01:00
84be750ce6 admin: circle animation no more block click on refresh button 2025-03-27 12:28:16 +01:00
3881385c9e admin: List all existing association between users and teams 2025-03-27 12:28:16 +01:00
e44cac32ac admin: New button to refine teams colors 2025-03-27 12:28:16 +01:00
485e6b0173 admin: Able to import Cyberrange teams from interface 2025-03-27 12:28:16 +01:00
f1ada8ce99 admin: Use logo from challengeinfo in ui template 2025-03-27 12:28:16 +01:00
7e301b8ecb admin: Replace PKI page by authentication settings, refactor 2025-03-27 12:28:16 +01:00
4dcf1218d8 chore(deps): update module github.com/asticode/go-astisub to v0.34.0
Some checks are pending
continuous-integration/drone/push Build is running
2025-03-27 11:27:30 +00:00
3f5b7b9ed7 chore(deps): update alpine docker tag to v3.21
Some checks are pending
continuous-integration/drone/push Build is running
2025-03-27 11:27:16 +00:00
bc0570c2c7 chore(deps): update dependency eslint-config-prettier to v10.1.1
Some checks are pending
continuous-integration/drone/push Build is running
2025-03-27 11:27:00 +00:00
cd50a4b9d3 chore(deps): update dependency vite to v5.4.15
Some checks are pending
continuous-integration/drone/push Build is running
2025-03-27 11:26:33 +00:00
4734a8f047 chore(deps): update dependency sass-loader to v16.0.5
Some checks are pending
continuous-integration/drone/push Build is running
2025-03-27 11:26:18 +00:00
dadb84e8f9 admin: Dex config contains challenge name instead of hardcoded name
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-26 12:04:43 +01:00
801042e5cf fickit: Update linuxkit images 2025-03-26 11:55:36 +01:00
fca27b07fe admin: Also import team members from CyberRange 2025-03-26 11:25:06 +01:00
3fc765ccfa admin: Export logos present in challenge.json 2025-03-26 11:13:09 +01:00
590a55c395 libfic: Create a color randomization function 2025-03-25 18:54:36 +01:00
b62369f89f admin: New route to import teams from CyberRange format 2025-03-25 18:19:22 +01:00
cb4ceecbf5 challenge-sync-airbus: Refactor and prefer calling it cyberrange 2025-03-25 18:02:11 +01:00
98d9f2daf3 Keep repochecker on 3.19 (needed for grammalecte) 2025-03-25 12:04:09 +01:00
db1e2603fc chore(deps): update alpine docker tag to v3.21 2025-03-25 12:04:09 +01:00
0730a22daa chore(deps): update dependency @sveltejs/kit to v2.20.2 2025-03-25 12:04:09 +01:00
3467ca6db5 chore(deps): update dependency sass to v1.86.0 2025-03-25 12:04:09 +01:00
910adb123a chore(deps): update dependency prettier to v3.5.3 2025-03-25 12:04:09 +01:00
1551c11a00 chore(deps): update dependency eslint to v9.23.0 2025-03-25 12:04:09 +01:00
ed3e6b66de chore(deps): update dependency @sveltestrap/sveltestrap to v7.1.0 2025-03-25 12:04:09 +01:00
c21fd098a0 Remove useless file 2025-03-25 12:04:09 +01:00
7df675346c challenge-sync-airbus: 2025 API ready
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-24 19:31:59 +01:00
526d693ffd chore(deps): update module golang.org/x/crypto to v0.33.0
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-02-11 16:18:38 +00:00
df8a759134 chore(deps): update dependency sass to v1.84.0
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-11 12:56:59 +00:00
03e48b749c chore(deps): update dependency @sveltejs/kit to v2.17.1
Some checks are pending
continuous-integration/drone/push Build is pending
2025-02-11 12:56:55 +00:00
97f7e3fa59 chore(deps): update dependency @sveltestrap/sveltestrap to v7
Some checks are pending
continuous-integration/drone/push Build is pending
2025-02-11 12:56:18 +00:00
e421c91ac2 chore(deps): update module github.com/go-git/go-git/v5 to v5.13.2
Some checks are pending
continuous-integration/drone/push Build is pending
2025-02-11 12:56:05 +00:00
baccc54d02 chore(deps): update module golang.org/x/oauth2 to v0.26.0
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-05 02:55:02 +00:00
e8e87c9958 chore(deps): update module golang.org/x/image to v0.24.0
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-02-05 00:42:32 +00:00
08a31898df admin: New button to delete tries for a flag
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-04 19:08:44 +01:00
b409fa6806 admin: Retrieve stats on exercices 2025-02-04 19:08:44 +01:00
63b4cdc622 admin: Use non-breakable whitespaces 2025-02-04 19:08:44 +01:00
650f933993 admin: duration change impact the expected end 2025-02-04 19:08:44 +01:00
603b226955 fickit: Prepare team registration through checker 2025-02-04 19:08:44 +01:00
55e829fa64 fickit: Allow admin to remove submissions 2025-02-04 19:08:44 +01:00
45a0504c44 chore(deps): lock file maintenance
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-27 02:27:27 +00:00
ad7489e558 admin: Start compute flag stats
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-25 14:46:47 +01:00
57c3cd8fd6 admin: Fix mcq entry update 2025-01-24 23:49:37 +01:00
24686a6a24 admin: Fix check file on disk for compressed files 2025-01-24 23:49:37 +01:00
407b67f4c2 sync: Ensure placeholder and raw are not the same 2025-01-24 23:49:37 +01:00
c28d974105 fickit: Update images 2025-01-24 23:49:37 +01:00
ffb69663b6 fickit: Initiate sshd config with keys on first run 2025-01-24 23:49:37 +01:00
4ec4f47951 fickit: keep last metadata iso when dm-crypt key change 2025-01-24 23:49:37 +01:00
96707e3a29 configs: Detect mkisofs 2025-01-24 23:49:37 +01:00
a4001759f6 ui: Fix file disclaimer not showing 2025-01-24 23:49:37 +01:00
f15cd29f78 chore(deps): update dependency @sveltestrap/sveltestrap to v6.2.8
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-23 18:41:07 +00:00
7e41ddd664 chore(deps): update dependency eslint-config-prettier to v10
Some checks are pending
continuous-integration/drone/push Build is running
2025-01-23 18:40:58 +00:00
89b7710544 chore(deps): update dependency @sveltejs/kit to v2.16.1
Some checks are pending
continuous-integration/drone/push Build is running
2025-01-23 18:40:43 +00:00
79ec20d11c chore(deps): update dependency sass to v1.83.4
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-01-21 14:57:58 +00:00
09206df20a chore(deps): update module golang.org/x/crypto to v0.32.0
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-18 12:40:53 +00:00
58 changed files with 1612 additions and 1000 deletions

View file

@ -20,7 +20,7 @@ RUN go get -d -v ./admin && \
go build -v -buildmode=plugin -o repochecker/videos-rules.so ./repochecker/videos
FROM alpine:3.20
FROM alpine:3.21
RUN apk add --no-cache \
ca-certificates \

View file

@ -13,7 +13,7 @@ RUN go get -d -v ./checker && \
go build -v -buildvcs=false -o checker/checker ./checker
FROM alpine:3.20
FROM alpine:3.21
WORKDIR /srv

View file

@ -13,7 +13,7 @@ RUN go get -d -v ./dashboard && \
go build -v -buildvcs=false -o dashboard/dashboard ./dashboard
FROM alpine:3.20
FROM alpine:3.21
EXPOSE 8082

View file

@ -1,4 +1,4 @@
FROM alpine:3.20
FROM alpine:3.21
EXPOSE 67/udp
EXPOSE 69/udp

View file

@ -12,7 +12,7 @@ RUN go get -d -v ./evdist && \
go build -v -buildvcs=false -o evdist/evdist ./evdist
FROM alpine:3.20
FROM alpine:3.21
WORKDIR /srv

View file

@ -13,7 +13,7 @@ RUN go get -d -v ./generator && \
go build -v -buildvcs=false -o generator/generator ./generator
FROM alpine:3.20
FROM alpine:3.21
WORKDIR /srv

View file

@ -15,7 +15,7 @@ RUN go get -d -v ./admin && \
go build -v -o get-remote-files ./admin/get-remote-files
FROM alpine:3.20
FROM alpine:3.21
RUN apk add --no-cache \
ca-certificates

View file

@ -25,7 +25,7 @@ RUN go get -d -v ./qa && \
go build -v -buildvcs=false -o qa/qa ./qa
FROM alpine:3.20
FROM alpine:3.21
EXPOSE 8083

View file

@ -13,7 +13,7 @@ RUN go get -d -v ./receiver && \
go build -v -buildvcs=false -o ./receiver/receiver ./receiver
FROM alpine:3.20
FROM alpine:3.21
EXPOSE 8080

View file

@ -13,7 +13,7 @@ RUN go get -d -v ./remote/challenge-sync-airbus && \
go build -v -buildvcs=false -o ./challenge-sync-airbus ./remote/challenge-sync-airbus
FROM alpine:3.20
FROM alpine:3.21
RUN apk add --no-cache openssl ca-certificates

View file

@ -13,7 +13,7 @@ RUN go get -d -v ./remote/scores-sync-zqds && \
go build -v -buildvcs=false -o ./scores-sync-zqds ./remote/scores-sync-zqds
FROM alpine:3.20
FROM alpine:3.21
RUN apk add --no-cache openssl ca-certificates

View file

@ -62,6 +62,8 @@ func declareExercicesRoutes(router *gin.RouterGroup) {
apiFlagsRoutes.POST("/try", tryExerciceFlag)
apiFlagsRoutes.DELETE("/", deleteExerciceFlag)
apiFlagsRoutes.GET("/dependancies", showExerciceFlagDeps)
apiFlagsRoutes.GET("/statistics", showExerciceFlagStats)
apiFlagsRoutes.DELETE("/tries", deleteExerciceFlagTries)
apiFlagsRoutes.GET("/choices/", listFlagChoices)
apiFlagsChoicesRoutes := apiExercicesRoutes.Group("/choices/:cid")
apiFlagsChoicesRoutes.Use(FlagChoiceHandler)
@ -77,6 +79,8 @@ func declareExercicesRoutes(router *gin.RouterGroup) {
apiQuizRoutes.PUT("", updateExerciceQuiz)
apiQuizRoutes.DELETE("", deleteExerciceQuiz)
apiQuizRoutes.GET("/dependancies", showExerciceQuizDeps)
apiQuizRoutes.GET("/statistics", showExerciceQuizStats)
apiQuizRoutes.DELETE("/tries", deleteExerciceQuizTries)
apiExercicesRoutes.GET("/tags", listExerciceTags)
apiExercicesRoutes.POST("/tags", addExerciceTag)
@ -852,6 +856,60 @@ func showExerciceFlagDeps(c *gin.Context) {
c.JSON(http.StatusOK, deps)
}
func showExerciceFlagStats(c *gin.Context) {
exercice := c.MustGet("exercice").(*fic.Exercice)
flag := c.MustGet("flag-key").(*fic.FlagKey)
history, err := exercice.GetHistory()
if err != nil {
log.Println("Unable to getExerciceHistory:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving exercice history"})
return
}
var completed int64
for _, hline := range history {
if hline["kind"].(string) == "flag_found" {
if *hline["secondary"].(*int) == flag.Id {
completed += 1
}
}
}
tries, err := flag.NbTries()
if err != nil {
log.Println("Unable to nbTries:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag tries"})
return
}
teams, err := flag.TeamsOnIt()
if err != nil {
log.Println("Unable to teamsOnIt:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag related teams"})
return
}
c.JSON(http.StatusOK, gin.H{
"completed": completed,
"tries": tries,
"teams": teams,
})
}
func deleteExerciceFlagTries(c *gin.Context) {
flag := c.MustGet("flag-key").(*fic.FlagKey)
err := flag.DeleteTries()
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
c.AbortWithStatusJSON(http.StatusOK, true)
}
func tryExerciceFlag(c *gin.Context) {
flag := c.MustGet("flag-key").(*fic.FlagKey)
@ -895,6 +953,23 @@ func updateExerciceFlag(c *gin.Context) {
flag.Help = uk.Help
flag.IgnoreCase = uk.IgnoreCase
flag.Multiline = uk.Multiline
flag.ChoicesCost = uk.ChoicesCost
flag.BonusGain = uk.BonusGain
if uk.CaptureRe != nil && len(*uk.CaptureRe) > 0 {
if flag.CaptureRegexp != uk.CaptureRe && uk.Flag == "" {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Pour changer la capture_regexp, vous devez rentrer la réponse attendue à nouveau, car le flag doit être recalculé."})
return
}
flag.CaptureRegexp = uk.CaptureRe
} else {
if flag.CaptureRegexp != nil && uk.Flag == "" {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Pour changer la capture_regexp, vous devez rentrer la réponse attendue à nouveau, car le flag doit être recalculé."})
return
}
flag.CaptureRegexp = nil
}
if len(uk.Flag) > 0 {
var err error
flag.Checksum, err = flag.ComputeChecksum([]byte(uk.Flag))
@ -906,14 +981,6 @@ func updateExerciceFlag(c *gin.Context) {
} else {
flag.Checksum = uk.Value
}
flag.ChoicesCost = uk.ChoicesCost
flag.BonusGain = uk.BonusGain
if uk.CaptureRe != nil && len(*uk.CaptureRe) > 0 {
flag.CaptureRegexp = uk.CaptureRe
} else {
flag.CaptureRegexp = nil
}
if _, err := flag.Update(); err != nil {
log.Println("Unable to updateExerciceFlag:", err.Error())
@ -1022,6 +1089,60 @@ func showExerciceQuizDeps(c *gin.Context) {
c.JSON(http.StatusOK, deps)
}
func showExerciceQuizStats(c *gin.Context) {
exercice := c.MustGet("exercice").(*fic.Exercice)
quiz := c.MustGet("flag-quiz").(*fic.MCQ)
history, err := exercice.GetHistory()
if err != nil {
log.Println("Unable to getExerciceHistory:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving exercice history"})
return
}
var completed int64
for _, hline := range history {
if hline["kind"].(string) == "mcq_found" {
if *hline["secondary"].(*int) == quiz.Id {
completed += 1
}
}
}
tries, err := quiz.NbTries()
if err != nil {
log.Println("Unable to nbTries:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag tries"})
return
}
teams, err := quiz.TeamsOnIt()
if err != nil {
log.Println("Unable to teamsOnIt:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when retrieving flag related teams"})
return
}
c.JSON(http.StatusOK, gin.H{
"completed": completed,
"tries": tries,
"teams": teams,
})
}
func deleteExerciceQuizTries(c *gin.Context) {
quiz := c.MustGet("flag-quiz").(*fic.MCQ)
err := quiz.DeleteTries()
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
c.AbortWithStatusJSON(http.StatusOK, true)
}
func updateExerciceQuiz(c *gin.Context) {
quiz := c.MustGet("flag-quiz").(*fic.MCQ)

View file

@ -3,9 +3,12 @@ package api
import (
"archive/zip"
"encoding/json"
"io"
"log"
"net/http"
"path"
"srs.epita.fr/fic-server/admin/sync"
"srs.epita.fr/fic-server/libfic"
"srs.epita.fr/fic-server/settings"
@ -59,6 +62,41 @@ func declareExportRoutes(router *gin.RouterGroup) {
json.NewEncoder(f).Encode(challengeinfo)
}
// Include partners' logos from challenge.json
if sync.GlobalImporter != nil {
if len(challengeinfo.MainLogo) > 0 {
for _, logo := range challengeinfo.MainLogo {
fd, closer, err := sync.OpenOrGetFile(sync.GlobalImporter, logo)
if err != nil {
log.Printf("Unable to archive main logo %q: %s", logo, err.Error())
continue
}
f, err := w.Create(path.Join("logo", path.Base(logo)))
if err == nil {
io.Copy(f, fd)
}
closer()
}
}
if len(challengeinfo.Partners) > 0 {
for _, partner := range challengeinfo.Partners {
fd, closer, err := sync.OpenOrGetFile(sync.GlobalImporter, partner.Src)
if err != nil {
log.Printf("Unable to archive partner logo %q: %s", partner.Src, err.Error())
continue
}
f, err := w.Create(path.Join("partner", path.Base(partner.Src)))
if err == nil {
io.Copy(f, fd)
}
closer()
}
}
}
// my.json
f, err = w.Create("my.json")
if err == nil {

View file

@ -34,6 +34,11 @@ func declarePasswordRoutes(router *gin.RouterGroup) {
c.JSON(http.StatusOK, gin.H{"password": passwd})
})
router.GET("/oauth-status", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"secret_defined": OidcSecret != "",
})
})
router.GET("/dex.yaml", func(c *gin.Context) {
cfg, err := genDexConfig()
if err != nil {
@ -136,7 +141,7 @@ storage:
web:
http: 0.0.0.0:5556
frontend:
issuer: Challenge forensic
issuer: {{ .Name }}
logoURL: {{ .LogoPath }}
dir: /srv/dex/web/
oauth2:

View file

@ -1,9 +1,9 @@
package api
import (
"encoding/json"
"fmt"
"log"
"math/rand"
"net/http"
"strconv"
"strings"
@ -186,6 +186,9 @@ func declareTeamsRoutes(router *gin.RouterGroup) {
declareTeamsPasswordRoutes(apiTeamsRoutes)
declareTeamClaimsRoutes(apiTeamsRoutes)
declareTeamCertificateRoutes(apiTeamsRoutes)
// Import teams from cyberrange
router.POST("/cyberrange-teams.json", importTeamsFromCyberrange)
}
func TeamHandler(c *gin.Context) {
@ -292,6 +295,11 @@ func bindingTeams(c *gin.Context) {
c.String(http.StatusOK, ret)
}
type teamAssociation struct {
Association string `json:"association"`
TeamId int64 `json:"team_id"`
}
func allAssociations(c *gin.Context) {
teams, err := fic.GetTeams()
if err != nil {
@ -300,7 +308,7 @@ func allAssociations(c *gin.Context) {
return
}
var ret []string
var ret []teamAssociation
for _, team := range teams {
assocs, err := pki.GetTeamAssociations(TeamsDir, team.Id)
@ -310,13 +318,84 @@ func allAssociations(c *gin.Context) {
}
for _, a := range assocs {
ret = append(ret, a)
ret = append(ret, teamAssociation{a, team.Id})
}
}
c.JSON(http.StatusOK, ret)
}
func importTeamsFromCyberrange(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"errmsg": "Failed to get file: " + err.Error()})
return
}
src, err := file.Open()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Failed to open file: " + err.Error()})
return
}
defer src.Close()
var ut []fic.CyberrangeTeam
err = json.NewDecoder(src).Decode(&fic.CyberrangeAPIResponse{Data: &ut})
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
teams, err := fic.GetTeams()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible de récupérer la liste des équipes actuelles: %s", err.Error())})
return
}
for _, crteam := range ut {
var exist_team *fic.Team
for _, team := range teams {
if team.Name == crteam.Name && team.ExternalId == crteam.UUID {
exist_team = team
break
}
}
if exist_team != nil {
exist_team.Name = crteam.Name
exist_team.ExternalId = crteam.UUID
_, err = exist_team.Update()
} else {
exist_team, err = fic.CreateTeam(crteam.Name, fic.RandomColor().ToRGB(), crteam.UUID)
}
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible d'ajouter/de modifier l'équipe %v: %s", crteam, err.Error())})
return
}
// Import members
if c.DefaultQuery("nomembers", "0") != "" && len(crteam.Members) > 0 {
exist_team.ClearMembers()
for _, member := range crteam.Members {
_, err = exist_team.AddMember(member.Name, "", member.Nickname, exist_team.Name)
if err != nil {
log.Printf("Unable to add member %q to team %s (tid=%d): %s", member.UUID, exist_team.Name, exist_team.Id, err.Error())
}
}
}
}
teams, err = fic.GetTeams()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible de récupérer la liste des équipes après import: %s", err.Error())})
return
}
c.JSON(http.StatusOK, teams)
}
func createTeam(c *gin.Context) {
var ut fic.Team
err := c.ShouldBindJSON(&ut)
@ -326,11 +405,7 @@ func createTeam(c *gin.Context) {
}
if ut.Color == 0 {
ut.Color = fic.HSL{
H: rand.Float64(),
S: 1,
L: 0.5,
}.ToRGB()
ut.Color = fic.RandomColor().ToRGB()
}
team, err := fic.CreateTeam(strings.TrimSpace(ut.Name), ut.Color, ut.ExternalId)

View file

@ -4,7 +4,7 @@ const indextpl = `<!DOCTYPE html>
<html ng-app="FICApp">
<head>
<meta charset="utf-8">
<title>Challenge Forensic - Administration</title>
<title>{{ .title }} - Administration</title>
<link href="{{.urlbase}}css/bootstrap.min.css" type="text/css" rel="stylesheet">
<link href="{{.urlbase}}css/glyphicon.css" type="text/css" rel="stylesheet" media="screen">
<style>
@ -86,7 +86,7 @@ const indextpl = `<!DOCTYPE html>
<body class="bg-light text-dark">
<nav class="navbar sticky-top navbar-expand-lg navbar-dark text-light" ng-class="{'bg-dark': settings.wip, 'bg-danger': !settings.wip}">
<a class="navbar-brand" href=".">
<img alt="FIC" src="img/fic.png" style="height: 30px">
<img alt="{{ .title }}" src="{{ .logo }}" style="height: 30px">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#adminMenu" aria-controls="adminMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
@ -95,7 +95,7 @@ const indextpl = `<!DOCTYPE html>
<div class="collapse navbar-collapse" id="adminMenu">
<ul class="navbar-nav mr-auto">
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/teams')}"><a class="nav-link" href="teams">&Eacute;quipes</a></li>
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/pki')}"><a class="nav-link" href="pki">PKI</a></li>
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/auth')}"><a class="nav-link" href="auth">Authentification</a></li>
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/themes')}"><a class="nav-link" href="themes">Thèmes</a></li>
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/exercices')}"><a class="nav-link" href="exercices">Exercices</a></li>
<li class="nav-item" ng-class="{'active': $location.path().startsWith('/public')}"><a class="nav-link" href="public/0">Public</a></li>
@ -124,7 +124,7 @@ const indextpl = `<!DOCTYPE html>
</div>
<span id="clock" class="navbar-text" ng-controller="CountdownController" ng-cloak>
<div style="position: absolute;">
<div style="pointer-events: none; position: absolute;">
<div style="position: absolute;" id="circle1" class="circle-anim border-danger"></div>
<div style="position: absolute;" id="circle2" class="circle-anim border-info"></div>
</div>

View file

@ -6,6 +6,7 @@ import (
"errors"
"log"
"net/http"
"os"
"path"
"strings"
"text/template"
@ -25,10 +26,24 @@ var assets embed.FS
var indexPage []byte
func genIndex(baseURL string) {
tplcfg := map[string]string{
"logo": "img/logo.png",
"title": "Challenge",
"urlbase": path.Clean(path.Join(baseURL+"/", "nuke"))[:len(path.Clean(path.Join(baseURL+"/", "nuke")))-4],
}
ci, err := api.GetChallengeInfo()
if err == nil && ci != nil {
tplcfg["title"] = ci.Title
if len(ci.MainLogo) > 0 {
tplcfg["logo"] = "/files/logo/" + path.Base(ci.MainLogo[0])
}
}
b := bytes.NewBufferString("")
if indexTmpl, err := template.New("index").Parse(indextpl); err != nil {
log.Fatal("Cannot create template:", err)
} else if err = indexTmpl.Execute(b, map[string]string{"urlbase": path.Clean(path.Join(baseURL+"/", "nuke"))[:len(path.Clean(path.Join(baseURL+"/", "nuke")))-4]}); err != nil {
} else if err = indexTmpl.Execute(b, tplcfg); err != nil {
log.Fatal("An error occurs during template execution:", err)
} else {
indexPage = b.Bytes()
@ -50,6 +65,9 @@ func declareStaticRoutes(router *gin.RouterGroup, cfg *settings.Settings, baseUR
router.GET("/", func(c *gin.Context) {
serveIndex(c)
})
router.GET("/auth/*_", func(c *gin.Context) {
serveIndex(c)
})
router.GET("/claims/*_", func(c *gin.Context) {
serveIndex(c)
})
@ -104,8 +122,22 @@ func declareStaticRoutes(router *gin.RouterGroup, cfg *settings.Settings, baseUR
})
router.GET("/files/*_", func(c *gin.Context) {
// TODO: handle .gz file here
http.ServeFile(c.Writer, c.Request, path.Join(fic.FilesDir, strings.TrimPrefix(c.Request.URL.Path, path.Join(baseURL, "files"))))
filepath := path.Join(fic.FilesDir, strings.TrimPrefix(strings.TrimPrefix(c.Request.URL.Path, baseURL), "/files"))
if st, err := os.Stat(filepath); os.IsNotExist(err) || st.Size() == 0 {
if st, err := os.Stat(filepath + ".gz"); err == nil {
if fd, err := os.Open(filepath + ".gz"); err == nil {
log.Println(filepath + ".gz")
c.DataFromReader(http.StatusOK, st.Size(), "application/octet-stream", fd, map[string]string{
"Content-Encoding": "gzip",
})
return
}
}
}
log.Println(filepath)
c.File(filepath)
})
router.GET("/submissions/*_", func(c *gin.Context) {
http.ServeFile(c.Writer, c.Request, path.Join(api.TimestampCheck, strings.TrimPrefix(c.Request.URL.Path, path.Join(baseURL, "submissions"))))

View file

@ -25,6 +25,10 @@ angular.module("FICApp", ["ngRoute", "ngResource", "ngSanitize"])
controller: "SettingsController",
templateUrl: "views/settings.html"
})
.when("/auth", {
controller: "AuthController",
templateUrl: "views/auth.html"
})
.when("/pki", {
controller: "PKIController",
templateUrl: "views/pki.html"
@ -348,6 +352,9 @@ angular.module("FICApp")
.factory("ExerciceFlagDeps", function ($resource) {
return $resource("api/exercices/:exerciceId/flags/:flagId/dependancies", { exerciceId: '@idExercice', flagId: '@id' })
})
.factory("ExerciceFlagStats", function ($resource) {
return $resource("api/exercices/:exerciceId/flags/:flagId/statistics", { exerciceId: '@idExercice', flagId: '@id' })
})
.factory("ExerciceMCQFlag", function ($resource) {
return $resource("api/exercices/:exerciceId/quiz/:mcqId", { exerciceId: '@idExercice', mcqId: '@id' }, {
update: { method: 'PUT' }
@ -355,6 +362,9 @@ angular.module("FICApp")
})
.factory("ExerciceMCQDeps", function ($resource) {
return $resource("api/exercices/:exerciceId/quiz/:mcqId/dependancies", { exerciceId: '@idExercice', mcqId: '@id' })
})
.factory("ExerciceMCQStats", function ($resource) {
return $resource("api/exercices/:exerciceId/quiz/:mcqId/statistics", { exerciceId: '@idExercice', mcqId: '@id' })
});
angular.module("FICApp")
@ -611,9 +621,18 @@ angular.module("FICApp")
response.enableExerciceDepend = response.unlockedChallengeDepth >= 0;
response.disabledsubmitbutton = response.disablesubmitbutton && response.disablesubmitbutton.length > 0;
if (response.end) {
$scope.duration = (response.end - response.start)/60000;
}
})
$scope.challenge = SettingsChallenge.get();
$scope.duration = 360;
$scope.durationChange = function(endChanged) {
if (endChanged)
$scope.duration = (new Date($scope.config.end).getTime() - new Date($scope.config.start).getTime())/60000;
else
$scope.config.end = new Date(new Date($scope.config.start).getTime() + $scope.duration * 60000);
}
$scope.activateTime = "";
$scope.challenge.$promise.then(function (c) {
if (c.duration)
@ -648,7 +667,7 @@ angular.module("FICApp")
$scope.saveChallengeInfo = function () {
this.challenge.duration = $scope.duration;
this.challenge.$update(function (response) {
$scope.addToast('success', 'Infos du challenge mises à jour avec succès !');
$scope.addToast('success', 'Infos du challenge mises à jour avec succès!');
}, function (response) {
$scope.addToast('danger', 'An error occurs when saving challenge info:', response.data.errmsg);
});
@ -722,9 +741,9 @@ angular.module("FICApp")
"teams": "En validant, vous supprimerez l'ensemble des équipes enregistreées.",
"game": "En validant, vous supprimerez toutes les tentatives, les validations, ... faites par les équipes.",
}
$scope.addToast('warning', txts[type], 'Êtes-vous sûr de vouloir continuer ?',
$scope.addToast('warning', txts[type], 'Êtes-vous sûr de vouloir continuer?',
function () {
if (confirm("Êtes-vous vraiment sûr ?\n" + txts[type])) {
if (confirm("Êtes-vous vraiment sûr?\n" + txts[type])) {
$http.post("api/reset", { "type": type }).then(function (time) {
$scope.addToast('success', type + 'reseted');
$location.url("/");
@ -736,7 +755,7 @@ angular.module("FICApp")
});
};
$scope.switchToProd = function () {
$scope.addToast('warning', "Activer le mode challenge ?", "L'activation du mode challenge est temporaire (vous devriez plutôt relancer le daemon avec l'option `-4real`). Ce mode permet d'éviter les mauvaises manipulations et désactive le hook git de synchronisation automatique. Êtes-vous sûr de vouloir continuer ?",
$scope.addToast('warning', "Activer le mode challenge?", "L'activation du mode challenge est temporaire (vous devriez plutôt relancer le daemon avec l'option `-4real`). Ce mode permet d'éviter les mauvaises manipulations et désactive le hook git de synchronisation automatique. Êtes-vous sûr de vouloir continuer?",
function () {
$http.put("api/prod", true).then(function (time) {
$rootScope.refreshSettings()
@ -759,6 +778,20 @@ angular.module("FICApp")
});
};
})
.component('teamLink', {
bindings: {
idTeam: '=',
},
controller: function (Team) {
var ctrl = this;
ctrl.team = {};
ctrl.$onInit = function () {
ctrl.team = Team.get({teamId: ctrl.idTeam});
};
},
template: `<a href="/teams/{{ $ctrl.idTeam }}">{{ $ctrl.team.name }}</a> `
})
.component('repositoryUptodate', {
bindings: {
repository: '<',
@ -820,10 +853,10 @@ angular.module("FICApp")
$scope.deepSync = function (theme) {
if (theme) {
question = 'Faire une synchronisation intégrale du thème ' + theme.name + ' ?'
question = 'Faire une synchronisation intégrale du thème ' + theme.name + '?'
url = "api/sync/deep/" + theme.id
} else {
question = 'Faire une synchronisation intégrale ?'
question = 'Faire une synchronisation intégrale?'
url = "api/sync/deep"
}
$scope.addToast('warning', question, '',
@ -839,7 +872,7 @@ angular.module("FICApp")
});
};
$scope.speedyDeepSync = function () {
$scope.addToast('warning', 'Faire une synchronisation profonde rapide, sans s\'occuper des fichiers ?', '',
$scope.addToast('warning', 'Faire une synchronisation profonde rapide, sans s\'occuper des fichiers?', '',
function () {
$scope.deepSyncInProgress = true;
$http.post("api/sync/speed").then(function () {
@ -852,7 +885,7 @@ angular.module("FICApp")
});
};
$scope.baseSync = function () {
$scope.addToast('warning', 'Tirer les mises à jour du dépôt ?', '',
$scope.addToast('warning', 'Tirer les mises à jour du dépôt?', '',
function () {
$scope.deepSyncInProgress = true;
$http.post("api/sync/base").then(function () {
@ -865,7 +898,7 @@ angular.module("FICApp")
});
};
$scope.syncVideos = function () {
$scope.addToast('warning', 'Synchroniser les vidéos de résolution ?', 'ATTENTION il ne faut pas lancer cette synchronisation durant le challenge. Seulement une fois le challenge terminé, cela permet de rendre les vidéos accessibles dans l\'interface joueurs.',
$scope.addToast('warning', 'Synchroniser les vidéos de résolution?', 'ATTENTION il ne faut pas lancer cette synchronisation durant le challenge. Seulement une fois le challenge terminé, cela permet de rendre les vidéos accessibles dans l\'interface joueurs.',
function () {
$scope.deepSyncInProgress = true;
$http.post("api/sync/videos").then(function () {
@ -892,6 +925,49 @@ angular.module("FICApp")
};
})
.controller("AuthController", function ($scope, $http) {
$scope.generateHtpasswd = function () {
$http.post("api/htpasswd").then(function () {
$scope.addToast('success', 'Fichier htpasswd généré avec succès');
}, function (response) {
$scope.addToast('danger', 'An error occurs when generating htpasswd file:', response.data.errmsg);
});
};
$scope.removeHtpasswd = function () {
$http.delete("api/htpasswd").then(function () {
$scope.addToast('success', 'Fichier htpasswd supprimé avec succès');
}, function (response) {
$scope.addToast('danger', 'An error occurs when deleting htpasswd file:', response.data.errmsg);
});
};
})
.controller("OAuthController", function ($scope, $http) {
$scope.refreshOAuthStatus = function () {
$http.get("api/oauth-status").then(function (res) {
$scope.oauth_status = response.data;
});
};
$scope.genDexCfg = function () {
$http.post("api/dex.yaml").then(function () {
$http.post("api/dex-password.tpl").then(function () {
$scope.addToast('success', 'Dex config refreshed.', "Don't forget to reload/reboot frontend host.");
}, function (response) {
$scope.addToast('danger', 'An error occurs when generating dex password tpl:', response.data.errmsg);
});
}, function (response) {
$scope.addToast('danger', 'An error occurs when generating dex config:', response.data.errmsg);
});
$http.post("api/vouch-proxy.yaml").then(function () {
$scope.addToast('success', 'VouchProxy config refreshed.', "Don't forget to reload/reboot frontend host.");
}, function (response) {
$scope.addToast('danger', 'An error occurs when generating VouchProxy config:', response.data.errmsg);
});
}
})
.controller("PKIController", function ($scope, $rootScope, Certificate, CACertificate, Team, $location, $http) {
var ts = Date.now() - Date.now() % 86400000;
var d = new Date(ts);
@ -976,20 +1052,6 @@ angular.module("FICApp")
$scope.addToast('danger', 'An error occurs when generating certificate:', response.data.errmsg);
});
};
$scope.generateHtpasswd = function () {
$http.post("api/htpasswd").then(function () {
$scope.addToast('success', 'Fichier htpasswd généré avec succès');
}, function (response) {
$scope.addToast('danger', 'An error occurs when generating htpasswd file:', response.data.errmsg);
});
};
$scope.removeHtpasswd = function () {
$http.delete("api/htpasswd").then(function () {
$scope.addToast('success', 'Fichier htpasswd supprimé avec succès');
}, function (response) {
$scope.addToast('danger', 'An error occurs when deleting htpasswd file:', response.data.errmsg);
});
};
})
.controller("PublicController", function ($scope, $rootScope, $routeParams, $location, Scene, Theme, Teams, Exercice) {
@ -1110,7 +1172,7 @@ angular.module("FICApp")
},
{
type: "countdown",
params: { color: "success", end: null, lead: "Go, go, go !", title: "Le challenge forensic va bientôt commencer !" },
params: { color: "success", end: null, lead: "Go, go, go!", title: "Le challenge forensic va bientôt commencer!" },
},
];
$scope.display.side = [
@ -1229,8 +1291,8 @@ angular.module("FICApp")
show: true,
shadow: "#E8CF5C",
end: new Date($rootScope.getSrvTime().getTime() + 1802000).toISOString(),
before: "Heure joyeuse : chaque résolution compte double !",
after: "Heure joyeuse terminée !",
before: "Heure joyeuse: chaque résolution compte double!",
after: "Heure joyeuse terminée!",
}
}
else if (scene == "freehintquarter") {
@ -1238,8 +1300,8 @@ angular.module("FICApp")
show: true,
shadow: "#3DD28F",
end: new Date($rootScope.getSrvTime().getTime() + 902000).toISOString(),
before: "Quart d'heure facile : indices dévoilés !",
after: "Quart d'heure facile terminée !",
before: "Quart d'heure facile: indices dévoilés!",
after: "Quart d'heure facile terminée!",
}
}
};
@ -1368,7 +1430,7 @@ angular.module("FICApp")
});
};
$scope.clearFilesDir = function () {
$scope.addToast('warning', 'Êtes-vous sûr de vouloir continuer ?', "Ceci va supprimer tout le contenu du dossier FILES. Il s'agit des fichiers ci-dessous, il faudra refaire une synchronisation ensuite.",
$scope.addToast('warning', 'Êtes-vous sûr de vouloir continuer?', "Ceci va supprimer tout le contenu du dossier FILES. Il s'agit des fichiers ci-dessous, il faudra refaire une synchronisation ensuite.",
function () {
$scope.clearFilesWIP = true;
$http({
@ -2255,9 +2317,9 @@ angular.module("FICApp")
}
$scope.saveFlag = function () {
if (this.flag.id) {
this.flag.$update();
this.flag.$update().then(function() {}, function(error) { $scope.addToast('danger', 'Impossible de mettre à jour le flag :', error.data.errmsg); });
} else {
this.flag.$save({ exerciceId: $routeParams.exerciceId });
this.flag.$save({ exerciceId: $routeParams.exerciceId }).then(function() {}, function(error) { $scope.addToast('danger', 'Impossible de créer le flag :', error.data.errmsg); });
}
$rootScope.staticFilesNeedUpdate++;
}
@ -2276,7 +2338,7 @@ angular.module("FICApp")
method: "POST"
}).then(function (response) {
flag.test_str = "";
$scope.addToast('success', "Flag Ok !");
$scope.addToast('success', "Flag Ok!");
}, function (response) {
flag.test_str = "";
$scope.addToast('danger', 'An error occurs: ', response.data.errmsg);
@ -2345,6 +2407,18 @@ angular.module("FICApp")
}
})
.controller("ExerciceFlagStatsController", function ($scope, $routeParams, ExerciceFlagStats, $http) {
$scope.init = function (flag) {
$scope.flag_id = flag.id;
$scope.stats = ExerciceFlagStats.get({ exerciceId: $routeParams.exerciceId, flagId: flag.id });
}
$scope.deleteTries = function () {
$http.delete(`/api/exercices/${$routeParams.exerciceId}/flags/${$scope.flag_id}/tries`).then(function () {
$scope.stats = ExerciceFlagStats.get({ exerciceId: $routeParams.exerciceId, flagId: $scope.flag_id });
});
}
})
.controller("ExerciceMCQFlagsController", function ($scope, ExerciceMCQFlag, $routeParams, $rootScope) {
$scope.quiz = ExerciceMCQFlag.query({ exerciceId: $routeParams.exerciceId });
@ -2382,6 +2456,18 @@ angular.module("FICApp")
}
})
.controller("ExerciceMCQStatsController", function ($scope, $routeParams, ExerciceMCQStats, $http) {
$scope.init = function (mcq) {
$scope.mcq_id = mcq.id;
$scope.stats = ExerciceMCQStats.get({ exerciceId: $routeParams.exerciceId, mcqId: mcq.id });
}
$scope.deleteTries = function () {
$http.delete(`/api/exercices/${$routeParams.exerciceId}/quiz/${$scope.mcq_id}/tries`).then(function () {
$scope.stats = ExerciceMCQStats.get({ exerciceId: $routeParams.exerciceId, mcqId: $scope.mcq_id });
});
}
})
.controller("TeamsListController", function ($scope, $rootScope, Team, $location, $http) {
$scope.teams = Team.query();
$scope.fields = ["id", "name"];
@ -2408,22 +2494,6 @@ angular.module("FICApp")
}
};
$scope.genDexCfg = function () {
$http.post("api/dex.yaml").then(function () {
$http.post("api/dex-password.tpl").then(function () {
$scope.addToast('success', 'Dex config refreshed.', "Don't forget to reload/reboot frontend host.");
}, function (response) {
$scope.addToast('danger', 'An error occurs when generating dex password tpl:', response.data.errmsg);
});
}, function (response) {
$scope.addToast('danger', 'An error occurs when generating dex config:', response.data.errmsg);
});
$http.post("api/vouch-proxy.yaml").then(function () {
$scope.addToast('success', 'VouchProxy config refreshed.', "Don't forget to reload/reboot frontend host.");
}, function (response) {
$scope.addToast('danger', 'An error occurs when generating VouchProxy config:', response.data.errmsg);
});
}
$scope.desactiveTeams = function () {
$http.post("api/disableinactiveteams").then(function () {
$scope.teams = Team.query();
@ -2431,9 +2501,34 @@ angular.module("FICApp")
$scope.addToast('danger', 'An error occurs when disabling inactive teams:', response.data.errmsg);
});
}
$scope.refineTeamsColors = function () {
$http.post("api/refine_colors").then(function () {
$scope.teams = Team.query();
}, function (response) {
$scope.addToast('danger', 'An error occurs when updating teams:', response.data.errmsg);
});
}
$scope.show = function (id) {
$location.url("/teams/" + id);
};
$scope.triggerTeamsImport = function() {
document.getElementById('crTeamsInput').click();
};
$scope.uploadFile = function() {
var formData = new FormData();
formData.append('file', $scope.selectedFile);
$http.post('api/cyberrange-teams.json', formData, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined},
}).then(function(response) {
$scope.teams = response.data;
$scope.addToast('success', 'Import des équipes', "L'import a été réalisé avec succès !");
}, function(error) {
console.log(error);
$scope.addToast('danger', 'Import des équipes', error.data.errmsg);
});
};
})
.controller("TeamMembersController", function ($scope, TeamMember) {
$scope.fields = ["firstname", "lastname", "nickname", "company"];

View file

@ -81,6 +81,22 @@ angular.module("FICApp")
});
}
}
}])
.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function($scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
$scope.$apply(function(){
modelSetter($scope, element[0].files[0]);
$scope.uploadFile();
});
});
}
};
}]);
angular.module("FICApp")

View file

@ -0,0 +1,86 @@
<div class="d-flex justify-content-between align-items-center">
<h2>
Authentification
</h2>
<div>
<div class="btn-group mr-1" role="group">
<button type="button" ng-click="generateHtpasswd()" class="btn btn-sm btn-secondary"><span class="glyphicon glyphicon-save-file" aria-hidden="true"></span> Générer <code>fichtpasswd</code></button>
<button type="button" ng-click="removeHtpasswd()" class="btn btn-sm btn-danger"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button>
</div>
</div>
</div>
<div ng-controller="OAuthController">
<div class="d-flex justify-content-between align-items-center">
<h3>
OAuth 2
<span class="badge badge-success" ng-if="oauth_status.secret_defined">Actif</span>
<span class="badge badge-danger" ng-if="!oauth_status.secret_defined">Non configuré</span>
</h3>
<div>
<button type="button" ng-click="genDexCfg()" class="btn btn-success mr-2"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> DexIdP</button>
</div>
</div>
</div>
<hr>
<div ng-controller="PKIController">
<div class="d-flex justify-content-between align-items-center">
<h3>
Autorité de certification
<span class="badge badge-success" ng-if="ca.version">Générée</span>
<span class="badge badge-danger" ng-if="!ca.version">Introuvable</span>
</h3>
<div>
<a
class="btn btn-primary"
href="/pki"
>
<span class="glyphicon glyphicon-certificate" aria-hidden="true"></span>
Gérer la PKI
</a>
</div>
</div>
<div class="alert alert-info" ng-if="!ca.version">
<strong>Aucune CA n'a été générée pour le moment.</strong>
</div>
<dl ng-if="ca.version">
<ng-repeat ng-repeat="(k, v) in ca" class="row">
<dt class="col-3 text-right">{{ k }}</dt>
<dd class="col-9" ng-if="v.CommonName">/CN={{ v.CommonName }}/OU={{ v.OrganizationalUnit }}/O={{ v.Organization }}/L={{ v.Locality }}/P={{ v.Province }}/C={{ v.Country }}/</dd>
<dd class="col-9" ng-if="!v.CommonName">{{ v }}</dd>
</ng-repeat>
</dl>
</div>
<hr>
<div class="mb-4" ng-controller="AllTeamAssociationsController">
<div class="d-flex justify-content-between align-items-center">
<h3>
Association utilisateurs et équipes
</h3>
<div>
</div>
</div>
<table class="table table-sm table-hover" ng-controller="TeamsListController" >
<tr>
<th class="text-right">Utilisateur</th>
<th></th>
<th>Équipe</th>
</tr>
<tr ng-repeat="association in allAssociations">
<td class="text-right">{{ association.association }}</td>
<td class="text-center">&#11020;</td>
<td ng-repeat="team in teams" ng-if="team.id == association.team_id">
<a ng-href="teams/{{ team.id }}">
{{ team.name }}
</a>
</td>
</tr>
</table>
</div>

View file

@ -73,12 +73,28 @@
</div>
<div class="col-4">
<div ng-controller="ExerciceFlagDepsController" ng-init="init(flag)">
Dépendances&nbsp;:
<strong>Dépendances&nbsp;:</strong>
<ul ng-if="deps.length > 0">
<dependancy ng-repeat="dep in deps" dep="dep"></dependancy>
</ul>
<span ng-if="deps.length == 0"> sans</span>
</div>
<hr>
<div ng-controller="ExerciceFlagStatsController" ng-init="init(flag)">
<strong>Statistiques</strong>
<ul>
<li>Validés: {{ stats["completed"] }}</li>
<li>
Tentés: {{ stats["tries"] }}
<button type="button" ng-click="deleteTries()" class="btn btn-sm btn-danger" ng-if="stats['tries'] > 0"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button>
</li>
<li>
Équipes:
<span ng-if="stats['teams'].length == 0">aucune</span>
<team-link ng-repeat="team in stats['teams']" id-team="team"></team-link>
</li>
</ul>
</div>
</div>
<div class="col-4" ng-controller="ExerciceFlagChoicesController">
<div class="btn-toolbar justify-content-end mb-2" role="toolbar">
@ -168,6 +184,22 @@
<dependancy ng-repeat="dep in deps" dep="dep"></dependancy>
</ul>
<span ng-if="deps.length == 0"> sans</span>
<hr>
<div ng-controller="ExerciceMCQStatsController" ng-init="init(q)">
<strong>Statistiques</strong>
<ul>
<li>Validés: {{ stats["completed"] }}</li>
<li>
Tentés: {{ stats["tries"] }}
<button type="button" ng-click="deleteTries()" class="btn btn-sm btn-danger" ng-if="stats['tries'] > 0"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button>
</li>
<li>
Équipes:
<span ng-if="stats['teams'].length == 0">aucune</span>
<team-link ng-repeat="team in stats['teams']" id-team="team"></team-link>
</li>
</ul>
</div>
</div>
</form>
</div>

View file

@ -100,7 +100,7 @@
<form ng-submit="saveFile()" class="list-group-item bg-light text-dark" ng-repeat="file in files">
<div class="row form-group">
<input type="text" ng-model="file.name" class="col form-control form-control-sm" placeholder="Nom de fichier">
<a href="../files{{file.path}}" class="btn btn-sm btn-secondary col-auto"><span class="glyphicon glyphicon-download" aria-hidden="true"></span></a>
<a href="../files{{file.path}}" target="_self" class="btn btn-sm btn-secondary col-auto"><span class="glyphicon glyphicon-download" aria-hidden="true"></span></a>
<button type="submit" class="btn btn-sm btn-success col-auto"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button><br>
<button type="button" ng-click="deleteFile()" class="btn btn-sm btn-danger col-auto"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
</div>

View file

@ -30,13 +30,13 @@
<div class="spinner-border spinner-border-sm" role="status" ng-if="file.gunzipWIP"></div>
</button>
</td>
<td ng-repeat="field in fields">
<td ng-repeat="field in fields" class="text-truncate" style="max-width: 30vw" title="{{ file[field] }}">
{{ file[field] }}
<span ng-if="field == 'id' && file.err !== undefined && file.err !== true" title="{{ file.err }}" class="glyphicon glyphicon-exclamation-sign"></span>
</td>
<td>
{{ file.checksum | bto16 }}
<div ng-if="file.checksum_shown">{{ file.checksum_shown | bto16 }}</div>
<td style="max-width: 100px">
<div class="text-truncate" title="{{ file.checksum | bto16 }}">{{ file.checksum | bto16 }}</div>
<div class="text-truncate" ng-if="file.checksum_shown" title="{{ file.checksum_shown | bto16 }}">{{ file.checksum_shown | bto16 }}</div>
</td>
</tr>
</tbody>

View file

@ -35,7 +35,7 @@
<label for="startTime" class="col-sm-3 col-form-label col-form-label-sm" ng-class="{'text-primary font-weight-bold': config.start != dist_config.start}">Début du challenge</label>
<div class="col-sm-9">
<div class="input-group">
<input type="datetime-local" class="form-control form-control-sm" id="startTime" ng-model="config.start" ng-class="{'border-primary': config.start != dist_config.start}">
<input type="datetime-local" class="form-control form-control-sm" id="startTime" ng-model="config.start" ng-change="durationChange()" ng-class="{'border-primary': config.start != dist_config.start}">
<div class="input-group-append">
<button ng-click="launchChallenge()" class="btn btn-sm btn-secondary" type="button"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Lancer le challenge</button>
</div>
@ -46,14 +46,14 @@
<div class="form-group row">
<label for="endTime" class="col-sm-3 col-form-label col-form-label-sm" ng-class="{'text-primary font-weight-bold': config.end != dist_config.end}">Fin du challenge</label>
<div class="col-sm-6">
<input type="datetime-local" class="form-control form-control-sm" id="endTime" ng-model="config.end" ng-class="{'border-primary': config.end != dist_config.end}">
<input type="datetime-local" class="form-control form-control-sm" id="endTime" ng-model="config.end" ng-change="durationChange(true)" ng-class="{'border-primary': config.end != dist_config.end}">
</div>
<div class="col-sm-1 text-right">
<label for="duration" class="col-form-label col-form-label-sm">Durée</label>
</div>
<div class="col-sm-2">
<div class="input-group input-group-sm">
<input type="text" class="form-control form-control-sm" id="duration" ng-model="duration" integer>
<input type="number" class="form-control form-control-sm" id="duration" ng-model="duration" ng-change="durationChange()" integer>
<div class="input-group-append">
<span class="input-group-text">min</span>
</div>
@ -323,7 +323,7 @@
<form class="row" ng-controller="AllTeamAssociationsController" ng-submit="addDelegatedQA()">
<div class="col">
<select class="form-control form-control-sm" ng-model="newdqa">
<option ng-selected="newdqa == m" ng-repeat="(i,m) in allAssociations" ng-value="m">{{ m }}</option>
<option ng-selected="newdqa == m.association" ng-repeat="(i,m) in allAssociations" ng-value="m.association">{{ m.association }}</option>
</select>
</div>
<div class="col input-group">

View file

@ -1,11 +1,19 @@
<h2>
&Eacute;quipes
<button type="button" ng-click="show('new')" class="float-right btn btn-sm btn-primary"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Ajouter une équipe</button>
<button type="button" ng-click="show('print')" class="float-right btn btn-sm btn-secondary mr-2"><span class="glyphicon glyphicon-print" aria-hidden="true"></span> Imprimer les équipes</button>
<button type="button" ng-click="show('export')" class="float-right btn btn-sm btn-secondary mr-2"><span class="glyphicon glyphicon-export" aria-hidden="true"></span> Statistiques générales</button>
<button type="button" ng-click="desactiveTeams()" class="float-right btn btn-sm btn-danger mr-2" title="Cliquer pour marquer les équipes sans certificat comme inactives (et ainsi éviter que ses fichiers ne soient générés)"><span class="glyphicon glyphicon-leaf" aria-hidden="true"></span> Désactiver les équipes inactives</button>
<button type="button" ng-click="genDexCfg()" class="float-right btn btn-sm btn-success mr-2"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> DexIdP</button>
</h2>
<div class="d-flex justify-content-between align-items-center">
<h2>
&Eacute;quipes
</h2>
<div>
<button type="button" ng-click="show('new')" class="btn btn-sm btn-primary ml-1"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Ajouter une équipe</button>
<form class="d-inline">
<input id="crTeamsInput" type="file" file-model="selectedFile" class="d-none" />
<button type="button" ng-click="triggerTeamsImport()" class="btn btn-sm btn-secondary ml-1"><span class="glyphicon glyphicon-import" aria-hidden="true"></span> Import Cyberrange</button>
</form>
<button type="button" ng-click="show('print')" class="btn btn-sm btn-secondary ml-1" title="Imprimer les équipes et leurs membres"><span class="glyphicon glyphicon-print" aria-hidden="true"></span></button>
<button type="button" ng-click="refineTeamsColors()" class="btn btn-sm btn-secondary ml-1" title="Réarranger automatiquement les couleurs des équipes pour maximiser le spectre utilisé"><span class="glyphicon glyphicon-adjust" aria-hidden="true"></span></button>
<button type="button" ng-click="show('export')" class="btn btn-sm btn-secondary ml-1"><span class="glyphicon glyphicon-export" aria-hidden="true"></span> Statistiques générales</button>
<button type="button" ng-click="desactiveTeams()" class="btn btn-sm btn-danger ml-1" title="Cliquer pour marquer les équipes sans certificat comme inactives (et ainsi éviter que ses fichiers ne soient générés)"><span class="glyphicon glyphicon-leaf" aria-hidden="true"></span> Désactiver les équipes inactives</button>
</div>
</div>
<p><input type="search" class="form-control" placeholder="Search" ng-model="query" ng-keypress="validateSearch($event)" autofocus></p>
<table class="table table-hover table-bordered table-striped table-sm">

View file

@ -279,6 +279,7 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
}
type importFlag struct {
origin ExerciceFlag
Line int
Flag fic.Flag
JustifyOf *fic.MCQ_entry
@ -392,8 +393,9 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
errs = multierr.Append(errs, berrs)
if addedFlag != nil {
ret = append(ret, importFlag{
Line: nline + 1,
Flag: addedFlag,
origin: flag,
Line: nline + 1,
Flag: addedFlag,
})
}
} else if flag.Type == "key" || strings.HasPrefix(flag.Type, "number") || flag.Type == "text" || flag.Type == "ucq" || flag.Type == "radio" || flag.Type == "vector" {
@ -401,6 +403,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
errs = multierr.Append(errs, berrs)
if addedFlag != nil {
ret = append(ret, importFlag{
origin: flag,
Line: nline + 1,
Flag: *addedFlag,
Choices: choices,
@ -462,6 +465,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
errs = multierr.Append(errs, berrs)
if addedFlag != nil {
ret = append(ret, importFlag{
origin: flag,
Line: nline + 1,
Flag: *addedFlag,
JustifyOf: entry,
@ -479,8 +483,9 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
}
ret = append([]importFlag{importFlag{
Line: nline + 1,
Flag: &addedFlag,
origin: flag,
Line: nline + 1,
Flag: &addedFlag,
}}, ret...)
}
return
@ -570,6 +575,10 @@ func CheckExerciceFlags(i Importer, exercice *fic.Exercice, files []string, exce
if int64(fk.ChoicesCost) >= exercice.Gain {
errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag's choice_cost is higher than exercice gain")))
}
if raw, ok := flag.origin.Raw.(string); ok && raw == fk.Placeholder {
errs = multierr.Append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag's placeholder and raw are identical")))
}
}
// Check dependency loop

View file

@ -238,3 +238,21 @@ func WriteFileContent(i Importer, URI string, content []byte) error {
return fmt.Errorf("%t is not capable of writing", i)
}
}
func OpenOrGetFile(i Importer, URI string) (fd io.Reader, closer func() error, err error) {
if strings.HasPrefix(URI, "$FILES$") {
var fdc io.ReadCloser
fdc, err = os.Open(path.Join(fic.FilesDir, strings.TrimPrefix(URI, "$FILES$/")))
fd = fdc
closer = fdc.Close
} else {
fd, err = GlobalImporter.GetFile(URI)
if fdcloser, ok := fd.(io.ReadCloser); ok {
closer = fdcloser.Close
} else {
closer = func() error { return nil }
}
}
return
}

View file

@ -20,6 +20,8 @@ escape_newline () {
sed 's/$/\\n/g' | tr -d '\n'
}
which mkisofs > /dev/null 2> /dev/null || { echo "Please install genisoimage (Debian/Ubuntu) or cdrkit (Alpine)" >&2; exit 1; }
if [ $# -gt 0 ]
then
which jq > /dev/null 2> /dev/null || { echo "Please install jq" >&2; exit 1; }

View file

@ -21,6 +21,8 @@ OLD_KEY=$(cat /run/config/dm-crypt/key)
[ "${NEW_KEY}" != "${OLD_KEY}" ] && {
read -p "DM-CRYPT key changed in metadata, are you sure you want to erase it? (y/N) " V
[ "$V" != "y" ] && [ "$V" != "Y" ] && while true; do
mv /boot/imgs/fickit-metadata.iso /boot/imgs/fickit-metadata.iso.skipped
cp /boot/imgs/fickit-metadata.iso.bak /boot/imgs/fickit-metadata.iso
echo
echo "Metadata drive not erased"
echo

View file

@ -1,54 +1,54 @@
kernel:
#image: nemunaire/kernel:5.10.62-0b705d955f5e283f62583c4e227d64a7924c138f-amd64
image: linuxkit/kernel:6.6.13
image: linuxkit/kernel:6.6.71
cmdline: "console=ttyS0 console=tty0"
init:
- linuxkit/init:7135424f6836ee166d1199e88cfb95ee88efaf91
- linuxkit/runc:efcece75889aec4e2de0d95ba27ccc46438522b3
- linuxkit/containerd:ce79d5d4ab9c46f4763735c6e4ab5c51c3feb5d8
- linuxkit/ca-certificates:d4cc1b82c73d272e94d0e71ea375fe56b0c0626a
- linuxkit/getty:bae9e3d4861173bacf78f14a4fe44997a430d13b
- linuxkit/init:8eea386739975a43af558eec757a7dcb3a3d2e7b
- linuxkit/runc:667e7ea2c426a2460ca21e3da065a57dbb3369c9
- linuxkit/containerd:a988a1a8bcbacc2c0390ca0c08f949e2b4b5915d
- linuxkit/ca-certificates:7b32a26ca9c275d3ef32b11fe2a83dbd2aee2fdb
- linuxkit/getty:05eca453695984a69617f1f1f0bcdae7f7032967
- nemunaire/mdadm:04814350d71ba9417e1f861be1685de26adf7a67
- nemunaire/kexec:839b4eedfce02a56c581dec2383dc6faff120855
onboot:
- name: mod
image: linuxkit/modprobe:e3de97ac10970edee33faa78d9780117174bd1ac
image: linuxkit/modprobe:773ee174006ecbb412830e48889795bae40b62f9
command: ["/bin/sh", "-c", "modprobe xhci_pci ahci intel_lpss_pci i2c_i801 megaraid_sas tg3 bnxt_en"]
- name: sysctl
image: linuxkit/sysctl:c5f4b4895844b993dce4e8b35fd8263a6b557807
image: linuxkit/sysctl:5f56434b81004b50b47ed629b222619168c2bcdf
binds:
- /etc/sysctl.d/01-fic.conf:/etc/sysctl.d/01-fic.conf:ro
# Metadata
- name: metadata
image: linuxkit/metadata:f35b5aafc7d19bb6a44a900840727902dad78e44
image: linuxkit/metadata:4f81c0c3a2b245567fd7d32d799018c9614a9907
command: ["/usr/bin/metadata", "-v", "cdrom"]
# Filesystem
- name: swap
image: linuxkit/swap:8a1fd15d56b6ddf67d6d8ce25361178e1f36128b
image: linuxkit/swap:f4b8ffef87c8c72165bd8a92b790ac252ccf1821
command: ["/sbin/swapon", "/dev/sda3"]
- name: dm-crypt
image: linuxkit/dm-crypt:19fa6affe9da03afc91694e36d72a4924c65a0e0
image: linuxkit/dm-crypt:981fde241bb84616a5ba94c04cdefa1489431a25
command: ["/usr/bin/crypto", "-l", "crypt_fic", "/dev/sda4"]
binds:
- /dev:/dev
- /run/config/dm-crypt:/etc/dm-crypt
- name: mount
image: linuxkit/mount:4413ebd50bfbe026058e4a60463259cece2b8bb5
image: linuxkit/mount:cb8caa72248f7082fc2074ce843d53cdc15df04a
command: ["/usr/bin/mountie", "-device", "/dev/mapper/crypt_fic", "/var/lib/fic" ]
# Network
# - name: dhcpcd
# image: linuxkit/dhcpcd:330839488cd122db3c44738e265c035c9729a963
# image: linuxkit/dhcpcd:157df9ef45a035f1542ec2270e374f18efef98a5
# command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"]
# - name: ntp
# image: linuxkit/openntpd:da26954c2f98a274091e5ed0bbdd2079a77a47c1
# image: linuxkit/openntpd:f99c4117763480815553b72022b426639a13ce86
- name: synchro-ip-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip a add 10.10.10.1/29 dev eth2; ip link set eth2 up;" ]
net: new
runtime:
@ -57,7 +57,7 @@ onboot:
bindNS:
net: /run/netns/synchro
- name: qa-ip-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip link show eth1 2> /dev/null && { ip a add 10.10.10.1/29 dev eth1; ip link set eth1 up; }; ip a add 172.17.0.6/24 dev vethin-qa; ip link set vethin-qa up" ]
net: new
runtime:
@ -69,7 +69,7 @@ onboot:
bindNS:
net: /run/netns/fic-qa
- name: admin-ip-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
#command: ["/bin/sh", "-c", "ip link add link eth3 name adminiface type vlan id 99; ip a add 172.16.99.219/24 dev adminiface; ip link set eth3 up; ip link set adminiface up; ip r add default via 172.16.99.1; ip a add 172.17.0.2/24 dev vethin-admin; ip link set vethin-admin up; ping -W 10 -c 1 172.16.99.1;" ]
command: ["/bin/sh", "-c", "ip link set eth3 up; while read IP; do ip a add ${IP} dev eth3; done < /run/config/ip_config/backend-admin; ip r add default via $(cat /run/config/ip_config/backend-router); ip a add 172.17.0.2/24 dev vethin-admin; ip link set vethin-admin up; echo 'Waiting for' $(cat /run/config/ip_config/backend-router); ping -W 10 -c 1 $(cat /run/config/ip_config/backend-router); ip link show eth1 2> /dev/null && { ip a add 10.0.0.1/24 dev eth1; ip link set eth1 up; };" ]
net: new
@ -85,7 +85,7 @@ onboot:
bindNS:
net: /run/netns/fic-admin
- name: checker-ip-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip a add 172.17.0.3/24 dev vethin-checker; ip link set vethin-checker up;" ]
net: new
runtime:
@ -96,7 +96,7 @@ onboot:
bindNS:
net: /run/netns/fic-checker
- name: generator-ip-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip a add 172.17.0.5/24 dev vethin-generat; ip link set vethin-generat up;" ]
net: new
runtime:
@ -107,7 +107,7 @@ onboot:
bindNS:
net: /run/netns/fic-generator
- name: mysql-ip-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip a add 172.17.0.4/24 dev vethin-db; ip link set vethin-db up;" ]
net: new
runtime:
@ -118,7 +118,7 @@ onboot:
bindNS:
net: /run/netns/db
- name: bridge-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip a add 172.17.0.1/24 dev br0; ip link set veth-admin master br0; ip link set veth-checker master br0; ip link set veth-generator master br0; ip link set veth-db master br0; ip link set veth-qa master br0; ip link set br0 up; ip link set veth-admin up; ip link set veth-checker up; ip link set veth-generator up; ip link set veth-db up; ip link set veth-qa up;" ]
runtime:
interfaces:
@ -126,7 +126,7 @@ onboot:
add: bridge
- name: firewall-synchro
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-synchro.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6" ]
binds:
- /etc/iptables/rules-synchro.v4:/etc/iptables/rules-synchro.v4:ro
@ -136,7 +136,7 @@ onboot:
mkdir:
- /var/lib/fic/teams
- name: firewall-admin
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-admin.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6" ]
binds:
- /etc/iptables/rules-admin.v4:/etc/iptables/rules-admin.v4:ro
@ -153,17 +153,26 @@ onboot:
mkdir:
- /var/lib/fic/secrets
- name: create-ssh-keys
image: nemunaire/rsync:a3d76b2dd0a9ad73be44dc77ad765b20d96a3285
command: ["/bin/sh", "-c", "touch /etc/ssh/sshd_config && ssh-keygen -A"]
binds:
- /var/lib/fic/ssh:/etc/ssh
runtime:
mkdir:
- /var/lib/fic/ssh
services:
# - name: getty
# image: linuxkit/getty:bae9e3d4861173bacf78f14a4fe44997a430d13b
# image: linuxkit/getty:05eca453695984a69617f1f1f0bcdae7f7032967
# env:
# - INSECURE=true
# Enable acpi to shutdown on power events
- name: acpid
image: linuxkit/acpid:6379700e2f3341250432e37a4cac36e35c7caac8
image: linuxkit/acpid:6cb5575e487a8fcbd4c3eb6721c23299e6ea452f
- name: rngd
image: linuxkit/rngd:814d1a3a76e84eae01a94575c038fd22652f94e3
image: linuxkit/rngd:1a18f2149e42a0a1cb9e7d37608a494342c26032
- name: db
image: mariadb:11
command: ["/bin/bash", "/usr/local/bin/docker-entrypoint.sh", "mariadbd"]
@ -228,7 +237,7 @@ services:
- /var/lib/fic/generator:/srv/GENERATOR:ro
- /var/lib/fic/pki:/srv/PKI
- /var/lib/fic/settings:/srv/SETTINGS
- /var/lib/fic/submissions:/srv/submissions:ro
- /var/lib/fic/submissions:/srv/submissions
- /var/lib/fic/sync:/srv/SYNC
- /var/lib/fic/teams:/srv/TEAMS
net: /run/netns/fic-admin
@ -269,7 +278,10 @@ services:
binds:
- /etc/hosts:/etc/hosts:ro
- /var/lib/fic/generator:/srv/GENERATOR:ro
# Uncomment this to disallow registrations
- /var/lib/fic/teams:/srv/TEAMS:ro
# Uncomment this to allow registrations
#- /var/lib/fic/teams:/srv/TEAMS
- /var/lib/fic/secrets/mysql_password:/run/secrets/mysql_password:ro
- /var/lib/fic/settingsdist:/srv/SETTINGSDIST:ro
- /var/lib/fic/submissions:/srv/submissions
@ -361,7 +373,6 @@ services:
- /var/lib/fic/files
- /var/lib/fic/pki/shared
- /var/lib/fic/settingsdist
- /var/lib/fic/ssh
- /var/lib/fic/submissions
- /var/lib/fic/teams
- /var/log/frontend

View file

@ -1,6 +1,6 @@
kernel:
#image: nemunaire/kernel:5.10.62-0b705d955f5e283f62583c4e227d64a7924c138f-amd64
image: linuxkit/kernel:6.6.13
image: linuxkit/kernel:6.6.71
cmdline: "console=ttyS0 console=tty0"
init:

View file

@ -1,50 +1,50 @@
kernel:
#image: nemunaire/kernel:5.10.62-0b705d955f5e283f62583c4e227d64a7924c138f-amd64
image: linuxkit/kernel:6.6.13
image: linuxkit/kernel:6.6.71
cmdline: "console=ttyS0 console=tty0"
init:
- linuxkit/init:7135424f6836ee166d1199e88cfb95ee88efaf91
- linuxkit/runc:efcece75889aec4e2de0d95ba27ccc46438522b3
- linuxkit/containerd:ce79d5d4ab9c46f4763735c6e4ab5c51c3feb5d8
- linuxkit/ca-certificates:d4cc1b82c73d272e94d0e71ea375fe56b0c0626a
- linuxkit/getty:bae9e3d4861173bacf78f14a4fe44997a430d13b
- linuxkit/init:8eea386739975a43af558eec757a7dcb3a3d2e7b
- linuxkit/runc:667e7ea2c426a2460ca21e3da065a57dbb3369c9
- linuxkit/containerd:a988a1a8bcbacc2c0390ca0c08f949e2b4b5915d
- linuxkit/ca-certificates:7b32a26ca9c275d3ef32b11fe2a83dbd2aee2fdb
- linuxkit/getty:05eca453695984a69617f1f1f0bcdae7f7032967
- nemunaire/mdadm:04814350d71ba9417e1f861be1685de26adf7a67
- nemunaire/kexec:839b4eedfce02a56c581dec2383dc6faff120855
- nemunaire/fic-frontend-ui:latest
onboot:
- name: mod
image: linuxkit/modprobe:e3de97ac10970edee33faa78d9780117174bd1ac
image: linuxkit/modprobe:773ee174006ecbb412830e48889795bae40b62f9
command: ["/bin/sh", "-c", "modprobe xhci_pci ahci intel_lpss_pci i2c_i801 megaraid_sas tg3 bnxt_en"]
- name: sysctl
image: linuxkit/sysctl:c5f4b4895844b993dce4e8b35fd8263a6b557807
image: linuxkit/sysctl:5f56434b81004b50b47ed629b222619168c2bcdf
# Metadata
- name: metadata
image: linuxkit/metadata:f35b5aafc7d19bb6a44a900840727902dad78e44
image: linuxkit/metadata:4f81c0c3a2b245567fd7d32d799018c9614a9907
command: ["/usr/bin/metadata", "-v", "cdrom"]
# Filesystem
- name: swap
image: linuxkit/swap:8a1fd15d56b6ddf67d6d8ce25361178e1f36128b
image: linuxkit/swap:f4b8ffef87c8c72165bd8a92b790ac252ccf1821
command: ["/sbin/swapon", "/dev/sda3"]
- name: dm-crypt
image: linuxkit/dm-crypt:19fa6affe9da03afc91694e36d72a4924c65a0e0
image: linuxkit/dm-crypt:981fde241bb84616a5ba94c04cdefa1489431a25
command: ["/usr/bin/crypto", "-l", "crypt_fic", "/dev/sda4"]
binds:
- /dev:/dev
- /run/config/dm-crypt:/etc/dm-crypt
- name: mount
image: linuxkit/mount:4413ebd50bfbe026058e4a60463259cece2b8bb5
image: linuxkit/mount:cb8caa72248f7082fc2074ce843d53cdc15df04a
command: ["/usr/bin/mountie", "-device", "/dev/mapper/crypt_fic", "/var/lib/fic" ]
# Network
# - name: ntp
# image: linuxkit/openntpd:da26954c2f98a274091e5ed0bbdd2079a77a47c1
# image: linuxkit/openntpd:f99c4117763480815553b72022b426639a13ce86
- name: nginx-ip-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip a add 172.17.1.2/24 dev vethin-nginx; ip link set vethin-nginx up;" ]
net: new
runtime:
@ -55,7 +55,7 @@ onboot:
bindNS:
net: /run/netns/nginx
- name: frontal-ip-setup # without bonding
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip link set name bond-frontal eth3; ip link set bond-frontal up; while read IP; do ip a add ${IP} dev bond-frontal; done < /run/config/ip_config/frontend-players; ip r add default via $(cat /run/config/ip_config/frontend-router); ip link add link bond-frontal name internet type vlan id 4; ip a add 10.10.10.2/29 dev internet; ip link set internet up;" ]
net: /run/netns/nginx
binds:
@ -67,7 +67,7 @@ onboot:
- name: eth3
# - name: eth4
# - name: frontal-ip-setup # with bonding
# image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
# image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
# command: ["/bin/sh", "-c", "ip link set dev bond-frontal type bond mode balance-alb; ip link set bond-frontal up; ifenslave bond-frontal eth1 eth2 eth3 eth4; while read IP; do ip a add ${IP} dev bond-frontal; done < /run/config/ip_config/frontend-players; ip r add default via $(cat /run/config/ip_config/frontend-router); ip link add link bond-frontal name internet type vlan id 4; ip link set internet up; sysctl -w net.ipv4.ip_forward=1;" ]
# net: /run/netns/nginx
# binds:
@ -81,7 +81,7 @@ onboot:
# - name: bond-frontal
# add: bond
- name: receiver-ip-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip a add 172.17.1.3/24 dev vethin-receiver; ip link set vethin-receiver up;" ]
net: new
runtime:
@ -92,7 +92,7 @@ onboot:
bindNS:
net: /run/netns/fic-receiver
- name: sshd-ip-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip a add 10.10.10.2/29 dev eth2; ip link set eth2 up;" ]
net: new
runtime:
@ -101,7 +101,7 @@ onboot:
bindNS:
net: /run/netns/sshd
- name: auth-ip-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip a add 172.17.1.4/24 dev vethin-auth; ip link set vethin-auth up;" ]
net: new
runtime:
@ -112,7 +112,7 @@ onboot:
bindNS:
net: /run/netns/auth
- name: bridge-setup
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/sh", "-c", "ip a add 172.17.1.1/24 dev br0; ip link set veth-nginx master br0; ip link set veth-receiver master br0; ip link set veth-auth master br0; ip link set br0 up; ip link set veth-nginx up; ip link set veth-receiver up; ip link set veth-auth up;" ]
runtime:
interfaces:
@ -120,7 +120,7 @@ onboot:
add: bridge
- name: firewall-frontal
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-frontal.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6; [ -f /run/config/remote_sync/destination ] && /sbin/iptables -I OUTPUT 7 -o bond-frontal -d $(cat /run/config/remote_sync/destination | tr -d '\n') -p tcp -m tcp --dport https -j ACCEPT;" ]
binds:
- /etc/iptables/rules-frontal.v4:/etc/iptables/rules-frontal.v4:ro
@ -129,26 +129,35 @@ onboot:
- /run/config/remote_sync/:/run/config/remote_sync/:ro
net: /run/netns/nginx
- name: firewall-sshd
image: linuxkit/ip:af77c3f93143ff352a07ad5233d25a665012bcce
image: linuxkit/ip:9696394a7d57b384ae919662ae162c9152029156
command: ["/bin/bash", "-c", "/sbin/iptables-restore < /etc/iptables/rules-sshd.v4; /sbin/ip6tables-restore < /etc/iptables/rules.v6" ]
binds:
- /etc/iptables/rules-sshd.v4:/etc/iptables/rules-sshd.v4:ro
- /etc/iptables/rules.v6:/etc/iptables/rules.v6:ro
net: /run/netns/sshd
- name: create-ssh-keys
image: nemunaire/rsync:a3d76b2dd0a9ad73be44dc77ad765b20d96a3285
command: ["/bin/sh", "-c", "touch /etc/ssh/sshd_config && ssh-keygen -A"]
binds:
- /var/lib/fic/ssh:/etc/ssh
runtime:
mkdir:
- /var/lib/fic/ssh
services:
# - name: getty
# image: linuxkit/getty:bae9e3d4861173bacf78f14a4fe44997a430d13b
# image: linuxkit/getty:05eca453695984a69617f1f1f0bcdae7f7032967
# env:
# - INSECURE=true
# Enable acpi to shutdown on power events
- name: acpid
image: linuxkit/acpid:6379700e2f3341250432e37a4cac36e35c7caac8
image: linuxkit/acpid:6cb5575e487a8fcbd4c3eb6721c23299e6ea452f
- name: rngd
image: linuxkit/rngd:814d1a3a76e84eae01a94575c038fd22652f94e3
image: linuxkit/rngd:1a18f2149e42a0a1cb9e7d37608a494342c26032
- name: dhcpcd
image: linuxkit/dhcpcd:330839488cd122db3c44738e265c035c9729a963
image: linuxkit/dhcpcd:157df9ef45a035f1542ec2270e374f18efef98a5
net: /run/netns/nginx
binds:
- /etc/dhcpcd.conf:/dhcpcd.conf:ro
@ -257,7 +266,6 @@ services:
- /var/lib/fic/files
- /var/lib/fic/pki
- /var/lib/fic/settingsdist
- /var/lib/fic/ssh
- /var/lib/fic/submissions
- /var/lib/fic/teams
@ -280,7 +288,7 @@ services:
# net: /run/netns/nginx
- name: dexidp
image: ghcr.io/dexidp/dex:v2.41.1
image: ghcr.io/dexidp/dex:v2.42.0
net: /run/netns/auth
binds:
- /etc/hosts:/etc/hosts:ro
@ -294,7 +302,7 @@ services:
mkdir:
- /var/lib/fic/dex
- name: vouch-proxy
image: quay.io/vouch/vouch-proxy:alpine-0.39
image: quay.io/vouch/vouch-proxy:alpine-0.41
env:
- VOUCH_CONFIG=/etc/vouch/config.yml
net: /run/netns/auth

View file

@ -1,15 +1,15 @@
kernel:
#image: nemunaire/kernel:5.10.62-0b705d955f5e283f62583c4e227d64a7924c138f-amd64
image: linuxkit/kernel:6.6.13
image: linuxkit/kernel:6.6.71
cmdline: "console=ttyS0 console=tty0"
init:
- nemunaire/mdadm:04814350d71ba9417e1f861be1685de26adf7a67
- nemunaire/syslinux:086f221f281d577d300949aa1094fb20c5cd90dc
- linuxkit/format:3c858f0cf42a2b14441bfb5c266b78f14d2b75a4
- linuxkit/dm-crypt:19fa6affe9da03afc91694e36d72a4924c65a0e0
- linuxkit/metadata:f35b5aafc7d19bb6a44a900840727902dad78e44
- linuxkit/format:3fb088f60ed73ba4a15be41e44654b74112fd3f9
- linuxkit/dm-crypt:981fde241bb84616a5ba94c04cdefa1489431a25
- linuxkit/metadata:4f81c0c3a2b245567fd7d32d799018c9614a9907
- alpine:latest
files:

View file

@ -1,12 +1,12 @@
kernel:
#image: nemunaire/kernel:5.10.62-0b705d955f5e283f62583c4e227d64a7924c138f-amd64
image: linuxkit/kernel:6.6.13
image: linuxkit/kernel:6.6.71
cmdline: "console=ttyS0 console=tty0"
init:
- nemunaire/mdadm:04814350d71ba9417e1f861be1685de26adf7a67
- linuxkit/metadata:f35b5aafc7d19bb6a44a900840727902dad78e44
- linuxkit/metadata:4f81c0c3a2b245567fd7d32d799018c9614a9907
- alpine:latest

File diff suppressed because it is too large Load diff

View file

@ -12,10 +12,10 @@
"@sveltejs/adapter-static": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@sveltestrap/sveltestrap": "^6.2.1",
"@sveltestrap/sveltestrap": "^7.0.0",
"eslint": "^9.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-svelte": "^2.35.1",
"eslint-config-prettier": "^10.0.0",
"eslint-plugin-svelte": "^3.0.0",
"prettier": "^3.0.0",
"prettier-plugin-svelte": "^3.1.2",
"sass": "^1.51.0",

View file

@ -33,9 +33,9 @@
</h1>
<div style="min-width: 0">
<h4 class="fw-bold"><samp>{file.name}</samp></h4>
{#if file.disclamer}
<div class="file-disclamer text-warning">
{file.disclamer}
{#if file.disclaimer}
<div class="file-disclaimer text-warning">
{file.disclaimer}
</div>
{/if}
<nobr>
@ -61,10 +61,10 @@
{/if}
<style>
.file-disclamer {
.file-disclaimer {
display: none;
}
:global(.list-group-item:hover .file-disclamer) {
:global(.list-group-item:hover .file-disclaimer) {
display: block;
}
</style>

42
go.mod
View file

@ -1,16 +1,16 @@
module srs.epita.fr/fic-server
go 1.21
go 1.23.0
toolchain go1.23.4
toolchain go1.24.1
require (
github.com/BurntSushi/toml v1.4.0
github.com/asticode/go-astisub v0.32.0
github.com/BurntSushi/toml v1.5.0
github.com/asticode/go-astisub v0.34.0
github.com/cenkalti/dominantcolor v1.0.3
github.com/gin-contrib/sessions v1.0.2
github.com/gin-gonic/gin v1.10.0
github.com/go-git/go-git/v5 v5.13.1
github.com/go-git/go-git/v5 v5.14.0
github.com/go-sql-driver/mysql v1.8.1
github.com/google/gopacket v1.1.19
github.com/studio-b12/gowebdav v0.10.0
@ -18,17 +18,17 @@ require (
github.com/yuin/goldmark v1.7.8
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b
go.uber.org/multierr v1.11.0
golang.org/x/crypto v0.31.0
golang.org/x/image v0.23.0
golang.org/x/oauth2 v0.25.0
golang.org/x/crypto v0.36.0
golang.org/x/image v0.25.0
golang.org/x/oauth2 v0.28.0
gopkg.in/fsnotify.v1 v1.4.7
)
require (
dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.5 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/asticode/go-astikit v0.20.0 // indirect
github.com/asticode/go-astits v1.8.0 // indirect
@ -37,23 +37,23 @@ require (
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cloudflare/circl v1.6.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/disintegration/imaging v1.6.2 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.1 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/gorilla/context v1.1.2 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/sessions v1.2.2 // indirect
@ -69,20 +69,20 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/u2takey/go-utils v0.3.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.34.1 // indirect

57
go.sum
View file

@ -10,6 +10,8 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
@ -17,6 +19,8 @@ github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VM
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I=
@ -33,6 +37,8 @@ github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0k
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
@ -68,6 +74,8 @@ github.com/asticode/go-astisub v0.30.0 h1:z4k2Y+V+rlCE8qk3uw/nie56KXxJaL1/GwTP+9
github.com/asticode/go-astisub v0.30.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
github.com/asticode/go-astisub v0.32.0 h1:i1RHVQyTxSAuX0X3YC5zIyWruVZorS3cDXxqxYa0qss=
github.com/asticode/go-astisub v0.32.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
github.com/asticode/go-astisub v0.34.0 h1:owKNj0A9pc7YVW/rNy2MJZ1mf0L8DTdklZVfyZDhTWI=
github.com/asticode/go-astisub v0.34.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
github.com/asticode/go-astits v1.8.0 h1:rf6aiiGn/QhlFjNON1n5plqF3Fs025XLUwiQ0NB6oZg=
github.com/asticode/go-astits v1.8.0/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ=
github.com/aws/aws-sdk-go v1.38.20 h1:QbzNx/tdfATbdKfubBpkt84OM6oBkxQZRw6+bW2GyeA=
@ -104,6 +112,8 @@ github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEM
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
@ -113,6 +123,8 @@ github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53E
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -168,6 +180,8 @@ github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA=
github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE=
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
@ -199,6 +213,10 @@ github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZt
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
@ -243,12 +261,15 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
@ -352,6 +373,8 @@ github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI=
github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -382,6 +405,8 @@ github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -542,6 +567,12 @@ golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk=
golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=
@ -589,6 +620,10 @@ golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g=
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4=
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@ -657,6 +692,10 @@ golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y=
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
golang.org/x/oauth2 v0.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU=
@ -707,6 +746,10 @@ golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -718,6 +761,10 @@ golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -798,6 +845,12 @@ golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -847,6 +900,10 @@ golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

26
libfic/cyberrange.go Normal file
View file

@ -0,0 +1,26 @@
package fic
import ()
type CyberrangeAPIResponse struct {
Data interface{}
CurrentPage int `json:"current_page"`
PerPage int `json:"per_page"`
LastPage int `json:"last_page"`
Total int `json:"total"`
}
type CyberrangeTeam struct {
UUID string `json:"session_uuid"`
Members []CyberrangeTeamMember `json:"members"`
Name string `json:"name"`
Score int64 `json:"score"`
Rank int `json:"rank"`
}
type CyberrangeTeamMember struct {
UUID string `json:"session_uuid"`
Name string `json:"name"`
Nickname string `json:"nickname"`
EMail string `json:"email"`
}

View file

@ -414,6 +414,7 @@ CREATE TABLE IF NOT EXISTS exercice_solved(
}
if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_tries(
id_try INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
id_exercice INTEGER NOT NULL,
id_team INTEGER NOT NULL,
time TIMESTAMP NOT NULL,
@ -423,6 +424,26 @@ CREATE TABLE IF NOT EXISTS exercice_tries(
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
return err
}
if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_tries_flags(
id_try INTEGER NOT NULL,
id_flag INTEGER NOT NULL,
FOREIGN KEY(id_try) REFERENCES exercice_tries(id_try) ON DELETE CASCADE,
FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag) ON DELETE CASCADE
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
return err
}
if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_tries_mcq(
id_try INTEGER NOT NULL,
id_mcq INTEGER NOT NULL,
FOREIGN KEY(id_try) REFERENCES exercice_tries(id_try) ON DELETE CASCADE,
FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq) ON DELETE CASCADE
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
return err
}

View file

@ -3,6 +3,7 @@ package fic
import (
"errors"
"fmt"
"log"
"math"
"time"
)
@ -444,11 +445,25 @@ func (e *Exercice) GetOrdinal() (int, error) {
}
// NewTry registers a solving attempt for the given Team.
func (e *Exercice) NewTry(t *Team, cksum []byte) error {
if _, err := DBExec("INSERT INTO exercice_tries (id_exercice, id_team, time, cksum) VALUES (?, ?, ?, ?)", e.Id, t.Id, time.Now(), cksum); err != nil {
return err
func (e *Exercice) NewTry(t *Team, cksum []byte, flags ...Flag) (int64, error) {
if res, err := DBExec("INSERT INTO exercice_tries (id_exercice, id_team, time, cksum) VALUES (?, ?, ?, ?)", e.Id, t.Id, time.Now(), cksum); err != nil {
return 0, err
} else {
return nil
return res.LastInsertId()
}
}
func (e *Exercice) NewTryFlag(tryid int64, flags ...Flag) {
for _, flag := range flags {
if fk, ok := flag.(*FlagKey); ok {
if _, err := DBExec("INSERT INTO exercice_tries_flags (id_try, id_flag) VALUES (?, ?)", tryid, fk.Id); err != nil {
log.Println("Unable to add detailed try: ", err.Error())
}
} else if fm, ok := flag.(*MCQ); ok {
if _, err := DBExec("INSERT INTO exercice_tries_mcq (id_try, id_mcq) VALUES (?, ?)", tryid, fm.Id); err != nil {
log.Println("Unable to add detailed try: ", err.Error())
}
}
}
}
@ -550,7 +565,7 @@ func (e *Exercice) MCQSolved() (res []int64) {
// CheckResponse, given both flags and MCQ responses, figures out if thoses are correct (or if they are previously solved).
// In the meanwhile, CheckResponse registers good answers given (but it does not mark the challenge as solved at the end).
func (e *Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq map[int]bool, t *Team) (bool, error) {
if err := e.NewTry(t, cksum); err != nil {
if tryId, err := e.NewTry(t, cksum); err != nil {
return false, err
} else if flags, err := e.GetFlagKeys(); err != nil {
return false, err
@ -565,6 +580,10 @@ func (e *Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
// Check MCQs
for _, mcq := range mcqs {
if mcq.HasOneEntry(respmcq) {
e.NewTryFlag(tryId, mcq)
}
if d := mcq.Check(respmcq); d > 0 {
if !PartialValidation || t.HasPartiallySolved(mcq) == nil {
valid = false
@ -589,15 +608,21 @@ func (e *Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
for _, flag := range flags {
if res, ok := respflags[flag.Id]; !ok && (!PartialValidation || t.HasPartiallySolved(flag) == nil) {
valid = valid && flag.IsOptionnal()
} else if flag.Check([]byte(res)) != 0 {
if !PartialValidation || t.HasPartiallySolved(flag) == nil {
valid = valid && flag.IsOptionnal()
} else if ok {
if len(res) > 0 {
e.NewTryFlag(tryId, flag)
}
} else {
err := flag.FoundBy(t)
if err == nil {
// err is unicity issue, probably flag already found
goodResponses += 1
if flag.Check([]byte(res)) != 0 {
if !PartialValidation || t.HasPartiallySolved(flag) == nil {
valid = valid && flag.IsOptionnal()
}
} else {
err := flag.FoundBy(t)
if err == nil {
// err is unicity issue, probably flag already found
goodResponses += 1
}
}
}
}

View file

@ -68,7 +68,8 @@ func (e *Exercice) AppendHistoryItem(tId int64, kind string, secondary *int64) e
if kind == "tries" {
bid := make([]byte, 5)
binary.LittleEndian.PutUint32(bid, rand.Uint32())
return (&Exercice{Id: e.Id}).NewTry(team, bid)
_, err = (&Exercice{Id: e.Id}).NewTry(team, bid)
return err
} else if kind == "hint" && secondary != nil {
return team.OpenHint(&EHint{Id: *secondary})
} else if kind == "wchoices" && secondary != nil {

View file

@ -392,7 +392,12 @@ func (f *EFile) GetDepends() ([]Flag, error) {
// CheckFileOnDisk recalculates the hash of the file on disk.
func (f *EFile) CheckFileOnDisk() error {
if _, size, err := checkFileHash(path.Join(FilesDir, f.Path), f.Checksum); err != nil {
firstChecksum := f.Checksum
if len(f.ChecksumShown) > 0 {
firstChecksum = f.ChecksumShown
}
if _, size, err := checkFileHash(path.Join(FilesDir, f.Path), firstChecksum); size > 0 && err != nil {
return err
} else if size == 0 {
if _, _, err := checkFileHash(path.Join(FilesDir, f.Path+".gz"), f.Checksum); err != nil {
@ -400,9 +405,17 @@ func (f *EFile) CheckFileOnDisk() error {
} else {
return nil
}
} else {
return nil
} else if err != nil {
return err
}
if _, err := os.Stat(path.Join(FilesDir, f.Path+".gz")); !os.IsNotExist(err) {
if _, _, err = checkFileHash(path.Join(FilesDir, f.Path+".gz"), f.Checksum); err != nil {
return err
}
}
return nil
}
// GunzipFileOnDisk gunzip a compressed file.

View file

@ -14,6 +14,9 @@ type Flag interface {
Check(val interface{}) int
IsOptionnal() bool
FoundBy(t *Team) error
NbTries() (int64, error)
TeamsOnIt() ([]int64, error)
DeleteTries() error
}
// GetFlag returns a list of flags comming with the challenge.

View file

@ -230,6 +230,54 @@ func (k *FlagKey) RecoverId() (Flag, error) {
}
}
// NbTries returns the flag resolution statistics.
func (k *FlagKey) NbTries() (tries int64, err error) {
err = DBQueryRow("SELECT COUNT(*) AS tries FROM exercice_tries_flags WHERE id_flag = ?", k.Id).Scan(&tries)
return
}
func (k *FlagKey) TeamsOnIt() ([]int64, error) {
if rows, err := DBQuery("SELECT DISTINCT M.id_team FROM exercice_tries_flags F INNER JOIN exercice_tries T ON T.id_try = F.id_try INNER JOIN teams M ON M.id_team = T.id_team WHERE id_flag = ?", k.Id); err != nil {
return nil, err
} else {
defer rows.Close()
teams := []int64{}
for rows.Next() {
var idteam int64
if err := rows.Scan(&idteam); err != nil {
return nil, err
}
teams = append(teams, idteam)
}
return teams, nil
}
}
func (k *FlagKey) DeleteTries() error {
if rows, err := DBQuery("SELECT id_try FROM exercice_tries_flags WHERE id_flag = ?", k.Id); err != nil {
return err
} else {
defer rows.Close()
for rows.Next() {
var idtry int64
err = rows.Scan(&idtry)
if err != nil {
return err
}
_, err = DBExec("DELETE FROM exercice_tries WHERE id_try = ?", idtry)
if err != nil {
return err
}
}
return nil
}
}
// AddFlagKey creates and fills a new struct Flag, from a hashed flag, and registers it into the database.
func (k *FlagKey) Create(e *Exercice) (Flag, error) {
// Check the regexp compile

View file

@ -71,6 +71,18 @@ func (k *FlagLabel) RecoverId() (Flag, error) {
}
}
func (k *FlagLabel) NbTries() (int64, error) {
return 0, nil
}
func (k *FlagLabel) TeamsOnIt() ([]int64, error) {
return nil, nil
}
func (k *FlagLabel) DeleteTries() error {
return nil
}
// AddFlagLabel creates and fills a new struct Flag and registers it into the database.
func (k *FlagLabel) Create(e *Exercice) (Flag, error) {
if res, err := DBExec("INSERT INTO exercice_flag_labels (id_exercice, ordre, label, variant) VALUES (?, ?, ?, ?)", e.Id, k.Order, k.Label, k.Variant); err != nil {

View file

@ -136,6 +136,54 @@ func (m *MCQ) RecoverId() (Flag, error) {
}
}
// NbTries returns the MCQ resolution statistics.
func (m *MCQ) NbTries() (tries int64, err error) {
err = DBQueryRow("SELECT COUNT(*) AS tries FROM exercice_tries_mcq WHERE id_mcq = ?", m.Id).Scan(&tries)
return
}
func (m *MCQ) TeamsOnIt() ([]int64, error) {
if rows, err := DBQuery("SELECT DISTINCT M.id_team FROM exercice_tries_mcq F INNER JOIN exercice_tries T ON T.id_try = F.id_try INNER JOIN teams M ON M.id_team = T.id_team WHERE id_mcq = ?", m.Id); err != nil {
return nil, err
} else {
defer rows.Close()
teams := []int64{}
for rows.Next() {
var idteam int64
if err := rows.Scan(&idteam); err != nil {
return nil, err
}
teams = append(teams, idteam)
}
return teams, nil
}
}
func (m *MCQ) DeleteTries() error {
if rows, err := DBQuery("SELECT id_try FROM exercice_tries_mcq WHERE id_mcq = ?", m.Id); err != nil {
return err
} else {
defer rows.Close()
for rows.Next() {
var idtry int64
err = rows.Scan(&idtry)
if err != nil {
return err
}
_, err = DBExec("DELETE FROM exercice_tries WHERE id_try = ?", idtry)
if err != nil {
return err
}
}
return nil
}
}
// Create registers a MCQ into the database and recursively add its entries.
func (m *MCQ) Create(e *Exercice) (Flag, error) {
if res, err := DBExec("INSERT INTO exercice_mcq (id_exercice, ordre, title) VALUES (?, ?, ?)", e.Id, m.Order, m.Title); err != nil {
@ -207,7 +255,7 @@ func (m *MCQ) AddEntry(e *MCQ_entry) (*MCQ_entry, error) {
// Update applies modifications back to the database.
func (n *MCQ_entry) Update() (int64, error) {
if res, err := DBExec("UPDATE mcq_entries SET label = ?, response = ? WHERE id_mcq = ?", n.Label, n.Response, n.Id); err != nil {
if res, err := DBExec("UPDATE mcq_entries SET label = ?, response = ? WHERE id_mcq_entry = ?", n.Label, n.Response, n.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
@ -319,6 +367,24 @@ func (m *MCQ) IsOptionnal() bool {
return false
}
// Check if the given vals contains at least a response for the given MCQ.
func (m *MCQ) HasOneEntry(v interface{}) bool {
var vals map[int]bool
if va, ok := v.(map[int]bool); !ok {
return false
} else {
vals = va
}
for _, n := range m.Entries {
if _, ok := vals[n.Id]; ok {
return true
}
}
return false
}
// Check if the given vals are the expected ones to validate this flag.
func (m *MCQ) Check(v interface{}) int {
var vals map[int]bool

View file

@ -3,6 +3,7 @@ package fic
import (
"bytes"
"crypto/md5"
"math/rand"
"regexp"
"strings"
)
@ -187,3 +188,11 @@ func (c HSL) ToRGB() (rgb uint32) {
return r*65536 + g*256 + b
}
func RandomColor() HSL {
return HSL{
H: rand.Float64(),
S: 1,
L: 0.5,
}
}

480
qa/ui/package-lock.json generated
View file

@ -8,7 +8,7 @@
"name": "qa",
"version": "0.0.1",
"dependencies": {
"@sveltestrap/sveltestrap": "^6.2.1",
"@sveltestrap/sveltestrap": "^7.0.0",
"bootstrap": "^5.2.2",
"bootstrap-icons": "^1.9.1"
},
@ -17,8 +17,8 @@
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"eslint": "^9.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-svelte": "^2.35.1",
"eslint-config-prettier": "^10.0.0",
"eslint-plugin-svelte": "^3.0.0",
"prettier": "^3.0.0",
"prettier-plugin-svelte": "^3.1.2",
"svelte": "^5.0.0",
@ -472,13 +472,13 @@
}
},
"node_modules/@eslint/config-array": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz",
"integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
"integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^2.1.5",
"@eslint/object-schema": "^2.1.6",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
},
@ -486,10 +486,20 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/config-helpers": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz",
"integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz",
"integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==",
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
"integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@ -500,9 +510,9 @@
}
},
"node_modules/@eslint/eslintrc": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz",
"integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
"integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -524,9 +534,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.18.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz",
"integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==",
"version": "9.23.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz",
"integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==",
"dev": true,
"license": "MIT",
"engines": {
@ -534,9 +544,9 @@
}
},
"node_modules/@eslint/object-schema": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz",
"integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==",
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
"integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@ -544,13 +554,13 @@
}
},
"node_modules/@eslint/plugin-kit": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz",
"integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==",
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz",
"integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/core": "^0.10.0",
"@eslint/core": "^0.12.0",
"levn": "^0.4.1"
},
"engines": {
@ -610,9 +620,9 @@
}
},
"node_modules/@humanwhocodes/retry": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz",
"integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==",
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz",
"integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@ -689,9 +699,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz",
"integrity": "sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.0.tgz",
"integrity": "sha512-G2fUQQANtBPsNwiVFg4zKiPQyjVKZCUdQUol53R8E71J7AsheRMV/Yv/nB8giOcOVqP7//eB5xPqieBYZe9bGg==",
"cpu": [
"arm"
],
@ -703,9 +713,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz",
"integrity": "sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.0.tgz",
"integrity": "sha512-qhFwQ+ljoymC+j5lXRv8DlaJYY/+8vyvYmVx074zrLsu5ZGWYsJNLjPPVJJjhZQpyAKUGPydOq9hRLLNvh1s3A==",
"cpu": [
"arm64"
],
@ -717,9 +727,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz",
"integrity": "sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.0.tgz",
"integrity": "sha512-44n/X3lAlWsEY6vF8CzgCx+LQaoqWGN7TzUfbJDiTIOjJm4+L2Yq+r5a8ytQRGyPqgJDs3Rgyo8eVL7n9iW6AQ==",
"cpu": [
"arm64"
],
@ -731,9 +741,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz",
"integrity": "sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.0.tgz",
"integrity": "sha512-F9ct0+ZX5Np6+ZDztxiGCIvlCaW87HBdHcozUfsHnj1WCUTBUubAoanhHUfnUHZABlElyRikI0mgcw/qdEm2VQ==",
"cpu": [
"x64"
],
@ -745,9 +755,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz",
"integrity": "sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.0.tgz",
"integrity": "sha512-JpsGxLBB2EFXBsTLHfkZDsXSpSmKD3VxXCgBQtlPcuAqB8TlqtLcbeMhxXQkCDv1avgwNjF8uEIbq5p+Cee0PA==",
"cpu": [
"arm64"
],
@ -759,9 +769,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz",
"integrity": "sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.0.tgz",
"integrity": "sha512-wegiyBT6rawdpvnD9lmbOpx5Sph+yVZKHbhnSP9MqUEDX08G4UzMU+D87jrazGE7lRSyTRs6NEYHtzfkJ3FjjQ==",
"cpu": [
"x64"
],
@ -773,9 +783,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz",
"integrity": "sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.0.tgz",
"integrity": "sha512-3pA7xecItbgOs1A5H58dDvOUEboG5UfpTq3WzAdF54acBbUM+olDJAPkgj1GRJ4ZqE12DZ9/hNS2QZk166v92A==",
"cpu": [
"arm"
],
@ -787,9 +797,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz",
"integrity": "sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.0.tgz",
"integrity": "sha512-Y7XUZEVISGyge51QbYyYAEHwpGgmRrAxQXO3siyYo2kmaj72USSG8LtlQQgAtlGfxYiOwu+2BdbPjzEpcOpRmQ==",
"cpu": [
"arm"
],
@ -801,9 +811,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz",
"integrity": "sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.0.tgz",
"integrity": "sha512-r7/OTF5MqeBrZo5omPXcTnjvv1GsrdH8a8RerARvDFiDwFpDVDnJyByYM/nX+mvks8XXsgPUxkwe/ltaX2VH7w==",
"cpu": [
"arm64"
],
@ -815,9 +825,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz",
"integrity": "sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.0.tgz",
"integrity": "sha512-HJbifC9vex9NqnlodV2BHVFNuzKL5OnsV2dvTw6e1dpZKkNjPG6WUq+nhEYV6Hv2Bv++BXkwcyoGlXnPrjAKXw==",
"cpu": [
"arm64"
],
@ -829,9 +839,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz",
"integrity": "sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.0.tgz",
"integrity": "sha512-VAEzZTD63YglFlWwRj3taofmkV1V3xhebDXffon7msNz4b14xKsz7utO6F8F4cqt8K/ktTl9rm88yryvDpsfOw==",
"cpu": [
"loong64"
],
@ -843,9 +853,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz",
"integrity": "sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.0.tgz",
"integrity": "sha512-Sts5DST1jXAc9YH/iik1C9QRsLcCoOScf3dfbY5i4kH9RJpKxiTBXqm7qU5O6zTXBTEZry69bGszr3SMgYmMcQ==",
"cpu": [
"ppc64"
],
@ -857,9 +867,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz",
"integrity": "sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.0.tgz",
"integrity": "sha512-qhlXeV9AqxIyY9/R1h1hBD6eMvQCO34ZmdYvry/K+/MBs6d1nRFLm6BOiITLVI+nFAAB9kUB6sdJRKyVHXnqZw==",
"cpu": [
"riscv64"
],
@ -871,9 +881,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz",
"integrity": "sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.0.tgz",
"integrity": "sha512-8ZGN7ExnV0qjXa155Rsfi6H8M4iBBwNLBM9lcVS+4NcSzOFaNqmt7djlox8pN1lWrRPMRRQ8NeDlozIGx3Omsw==",
"cpu": [
"s390x"
],
@ -885,9 +895,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz",
"integrity": "sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.0.tgz",
"integrity": "sha512-VDzNHtLLI5s7xd/VubyS10mq6TxvZBp+4NRWoW+Hi3tgV05RtVm4qK99+dClwTN1McA6PHwob6DEJ6PlXbY83A==",
"cpu": [
"x64"
],
@ -899,9 +909,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz",
"integrity": "sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.0.tgz",
"integrity": "sha512-qcb9qYDlkxz9DxJo7SDhWxTWV1gFuwznjbTiov289pASxlfGbaOD54mgbs9+z94VwrXtKTu+2RqwlSTbiOqxGg==",
"cpu": [
"x64"
],
@ -913,9 +923,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz",
"integrity": "sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.0.tgz",
"integrity": "sha512-pFDdotFDMXW2AXVbfdUEfidPAk/OtwE/Hd4eYMTNVVaCQ6Yl8et0meDaKNL63L44Haxv4UExpv9ydSf3aSayDg==",
"cpu": [
"arm64"
],
@ -927,9 +937,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz",
"integrity": "sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.0.tgz",
"integrity": "sha512-/TG7WfrCAjeRNDvI4+0AAMoHxea/USWhAzf9PVDFHbcqrQ7hMMKp4jZIy4VEjk72AAfN5k4TiSMRXRKf/0akSw==",
"cpu": [
"ia32"
],
@ -941,9 +951,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz",
"integrity": "sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.0.tgz",
"integrity": "sha512-5hqO5S3PTEO2E5VjCePxv40gIgyS2KvO7E7/vvC/NbIW4SIRamkMr1hqj+5Y67fbBWv/bQLB6KelBQmXlyCjWA==",
"cpu": [
"x64"
],
@ -954,6 +964,15 @@
"win32"
]
},
"node_modules/@sveltejs/acorn-typescript": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz",
"integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
"license": "MIT",
"peerDependencies": {
"acorn": "^8.9.0"
}
},
"node_modules/@sveltejs/adapter-static": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz",
@ -965,25 +984,23 @@
}
},
"node_modules/@sveltejs/kit": {
"version": "2.15.2",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.15.2.tgz",
"integrity": "sha512-p208T1kdM6zd8k4YXIUM60pLWQ8dZqehXSiqn4NulXHyHibX53uIAL2xtNL8GjxX2IVPqPRT978MwVYhCKExdQ==",
"version": "2.20.2",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.2.tgz",
"integrity": "sha512-Dv8TOAZC9vyfcAB9TMsvUEJsRbklRTeNfcYBPaeH6KnABJ99i3CvCB2eNx8fiiliIqe+9GIchBg4RodRH5p1BQ==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@types/cookie": "^0.6.0",
"cookie": "^0.6.0",
"devalue": "^5.1.0",
"esm-env": "^1.2.1",
"esm-env": "^1.2.2",
"import-meta-resolve": "^4.1.0",
"kleur": "^4.1.5",
"magic-string": "^0.30.5",
"mrmime": "^2.0.0",
"sade": "^1.8.1",
"set-cookie-parser": "^2.6.0",
"sirv": "^3.0.0",
"tiny-glob": "^0.2.9"
"sirv": "^3.0.0"
},
"bin": {
"svelte-kit": "svelte-kit.js"
@ -1039,9 +1056,9 @@
}
},
"node_modules/@sveltestrap/sveltestrap": {
"version": "6.2.7",
"resolved": "https://registry.npmjs.org/@sveltestrap/sveltestrap/-/sveltestrap-6.2.7.tgz",
"integrity": "sha512-WwLLfAFUb42BGuRrf3Vbct30bQMzlEMMipN/MfxhjuLTmLQeW9muVJfPyvjtWS+mY+RjkSCoHvAp/ZobP1NLlQ==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@sveltestrap/sveltestrap/-/sveltestrap-7.1.0.tgz",
"integrity": "sha512-TpIx25kqLV+z+VD3yfqYayOI1IaCeWFbT0uqM6NfA4vQgDs9PjFwmjkU4YEAlV/ngs9e7xPmaRWE7lkrg4Miow==",
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.11.8"
@ -1092,15 +1109,6 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/acorn-typescript": {
"version": "1.4.13",
"resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz",
"integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==",
"license": "MIT",
"peerDependencies": {
"acorn": ">=8.9.0"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@ -1408,22 +1416,23 @@
}
},
"node_modules/eslint": {
"version": "9.18.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz",
"integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==",
"version": "9.23.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz",
"integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.19.0",
"@eslint/core": "^0.10.0",
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "9.18.0",
"@eslint/plugin-kit": "^0.2.5",
"@eslint/config-array": "^0.19.2",
"@eslint/config-helpers": "^0.2.0",
"@eslint/core": "^0.12.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.23.0",
"@eslint/plugin-kit": "^0.2.7",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
@ -1431,7 +1440,7 @@
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.2.0",
"eslint-scope": "^8.3.0",
"eslint-visitor-keys": "^4.2.0",
"espree": "^10.3.0",
"esquery": "^1.5.0",
@ -1468,9 +1477,9 @@
}
},
"node_modules/eslint-compat-utils": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz",
"integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==",
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.6.4.tgz",
"integrity": "sha512-/u+GQt8NMfXO8w17QendT4gvO5acfxQsAKirAt0LVxDnr2N8YLCVbregaNc/Yhp7NM128DwCaRvr8PLDfeNkQw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1484,9 +1493,9 @@
}
},
"node_modules/eslint-config-prettier": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
"integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz",
"integrity": "sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==",
"dev": true,
"license": "MIT",
"bin": {
@ -1497,32 +1506,31 @@
}
},
"node_modules/eslint-plugin-svelte": {
"version": "2.46.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.46.1.tgz",
"integrity": "sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==",
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.3.3.tgz",
"integrity": "sha512-imzGqIgWbfsb/CR14d3k3M8MiVNGet+l9mjPhvo1Rm0Nxi0rNn4/eELqyR8FWlgKBMlGkOp2kshRJm0xpxNfHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@jridgewell/sourcemap-codec": "^1.4.15",
"eslint-compat-utils": "^0.5.1",
"@eslint-community/eslint-utils": "^4.4.1",
"@jridgewell/sourcemap-codec": "^1.5.0",
"eslint-compat-utils": "^0.6.4",
"esutils": "^2.0.3",
"known-css-properties": "^0.35.0",
"postcss": "^8.4.38",
"postcss": "^8.4.49",
"postcss-load-config": "^3.1.4",
"postcss-safe-parser": "^6.0.0",
"postcss-selector-parser": "^6.1.0",
"semver": "^7.6.2",
"svelte-eslint-parser": "^0.43.0"
"postcss-safe-parser": "^7.0.0",
"semver": "^7.6.3",
"svelte-eslint-parser": "^1.0.1"
},
"engines": {
"node": "^14.17.0 || >=16.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://github.com/sponsors/ota-meshi"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0-0 || ^9.0.0-0",
"eslint": "^8.57.1 || ^9.0.0",
"svelte": "^3.37.0 || ^4.0.0 || ^5.0.0"
},
"peerDependenciesMeta": {
@ -1532,9 +1540,9 @@
}
},
"node_modules/eslint-scope": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
"integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
"integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@ -1599,9 +1607,9 @@
}
},
"node_modules/esrap": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.3.tgz",
"integrity": "sha512-Xddc1RsoFJ4z9nR7W7BFaEPIp4UXoeQ0+077UdWLxbafMQFyU79sQJMk7kxNgRwQ9/aVgaKacCHC2pUACGwmYw==",
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.5.tgz",
"integrity": "sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
@ -1753,20 +1761,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/globalyzer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz",
"integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==",
"dev": true,
"license": "MIT"
},
"node_modules/globrex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
"dev": true,
"license": "MIT"
},
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@ -2144,9 +2138,9 @@
"license": "ISC"
},
"node_modules/postcss": {
"version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"version": "8.5.1",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
"integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
"dev": true,
"funding": [
{
@ -2164,7 +2158,7 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@ -2203,20 +2197,30 @@
}
},
"node_modules/postcss-safe-parser": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz",
"integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz",
"integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss-safe-parser"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"engines": {
"node": ">=12.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
"node": ">=18.0"
},
"peerDependencies": {
"postcss": "^8.3.3"
"postcss": "^8.4.31"
}
},
"node_modules/postcss-scss": {
@ -2247,9 +2251,9 @@
}
},
"node_modules/postcss-selector-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2271,9 +2275,9 @@
}
},
"node_modules/prettier": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"license": "MIT",
"bin": {
@ -2318,9 +2322,9 @@
}
},
"node_modules/rollup": {
"version": "4.30.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz",
"integrity": "sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==",
"version": "4.32.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.0.tgz",
"integrity": "sha512-JmrhfQR31Q4AuNBjjAX4s+a/Pu/Q8Q9iwjWBsjRH1q52SPFE2NqRMK6fUZKKnvKO6id+h7JIRf0oYsph53eATg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2334,25 +2338,25 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.30.1",
"@rollup/rollup-android-arm64": "4.30.1",
"@rollup/rollup-darwin-arm64": "4.30.1",
"@rollup/rollup-darwin-x64": "4.30.1",
"@rollup/rollup-freebsd-arm64": "4.30.1",
"@rollup/rollup-freebsd-x64": "4.30.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.30.1",
"@rollup/rollup-linux-arm-musleabihf": "4.30.1",
"@rollup/rollup-linux-arm64-gnu": "4.30.1",
"@rollup/rollup-linux-arm64-musl": "4.30.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.30.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.30.1",
"@rollup/rollup-linux-riscv64-gnu": "4.30.1",
"@rollup/rollup-linux-s390x-gnu": "4.30.1",
"@rollup/rollup-linux-x64-gnu": "4.30.1",
"@rollup/rollup-linux-x64-musl": "4.30.1",
"@rollup/rollup-win32-arm64-msvc": "4.30.1",
"@rollup/rollup-win32-ia32-msvc": "4.30.1",
"@rollup/rollup-win32-x64-msvc": "4.30.1",
"@rollup/rollup-android-arm-eabi": "4.32.0",
"@rollup/rollup-android-arm64": "4.32.0",
"@rollup/rollup-darwin-arm64": "4.32.0",
"@rollup/rollup-darwin-x64": "4.32.0",
"@rollup/rollup-freebsd-arm64": "4.32.0",
"@rollup/rollup-freebsd-x64": "4.32.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.32.0",
"@rollup/rollup-linux-arm-musleabihf": "4.32.0",
"@rollup/rollup-linux-arm64-gnu": "4.32.0",
"@rollup/rollup-linux-arm64-musl": "4.32.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.32.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.32.0",
"@rollup/rollup-linux-riscv64-gnu": "4.32.0",
"@rollup/rollup-linux-s390x-gnu": "4.32.0",
"@rollup/rollup-linux-x64-gnu": "4.32.0",
"@rollup/rollup-linux-x64-musl": "4.32.0",
"@rollup/rollup-win32-arm64-msvc": "4.32.0",
"@rollup/rollup-win32-ia32-msvc": "4.32.0",
"@rollup/rollup-win32-x64-msvc": "4.32.0",
"fsevents": "~2.3.2"
}
},
@ -2464,16 +2468,16 @@
}
},
"node_modules/svelte": {
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.19.0.tgz",
"integrity": "sha512-qvd2GvvYnJxS/MteQKFSMyq8cQrAAut28QZ39ySv9k3ggmhw4Au4Rfcsqva74i0xMys//OhbhVCNfXPrDzL/Bg==",
"version": "5.25.3",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.25.3.tgz",
"integrity": "sha512-J9rcZ/xVJonAoESqVGHHZhrNdVbrCfkdB41BP6eiwHMoFShD9it3yZXApVYMHdGfCshBsZCKsajwJeBbS/M1zg==",
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.3.0",
"@jridgewell/sourcemap-codec": "^1.5.0",
"@sveltejs/acorn-typescript": "^1.0.5",
"@types/estree": "^1.0.5",
"acorn": "^8.12.1",
"acorn-typescript": "^1.4.13",
"aria-query": "^5.3.1",
"axobject-query": "^4.1.0",
"clsx": "^2.1.1",
@ -2489,20 +2493,21 @@
}
},
"node_modules/svelte-eslint-parser": {
"version": "0.43.0",
"resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.43.0.tgz",
"integrity": "sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.1.1.tgz",
"integrity": "sha512-QLVGPIMDettl30qRHXU2VrPvVJKG8GsGstye7n8rFbEiu3gEARksuQg9Xu4GzubNxhGNM8stfBZkhyMbBQmjFA==",
"dev": true,
"license": "MIT",
"dependencies": {
"eslint-scope": "^7.2.2",
"eslint-visitor-keys": "^3.4.3",
"espree": "^9.6.1",
"postcss": "^8.4.39",
"postcss-scss": "^4.0.9"
"eslint-scope": "^8.2.0",
"eslint-visitor-keys": "^4.0.0",
"espree": "^10.0.0",
"postcss": "^8.4.49",
"postcss-scss": "^4.0.9",
"postcss-selector-parser": "^7.0.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://github.com/sponsors/ota-meshi"
@ -2516,54 +2521,6 @@
}
}
},
"node_modules/svelte-eslint-parser/node_modules/eslint-scope": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/svelte-eslint-parser/node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/svelte-eslint-parser/node_modules/espree": {
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.9.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/svelte-hmr": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz",
@ -2577,17 +2534,6 @@
"svelte": "^3.19.0 || ^4.0.0"
}
},
"node_modules/tiny-glob": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
"integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==",
"dev": true,
"license": "MIT",
"dependencies": {
"globalyzer": "0.1.0",
"globrex": "^0.1.2"
}
},
"node_modules/totalist": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
@ -2629,9 +2575,9 @@
"license": "MIT"
},
"node_modules/vite": {
"version": "5.4.11",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
"integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
"version": "5.4.15",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.15.tgz",
"integrity": "sha512-6ANcZRivqL/4WtwPGTKNaosuNJr5tWiftOC7liM7G9+rMb8+oeJeyzymDu4rTN93seySBmbjSfsS3Vzr19KNtA==",
"dev": true,
"license": "MIT",
"dependencies": {

View file

@ -14,8 +14,8 @@
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"eslint": "^9.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-svelte": "^2.35.1",
"eslint-config-prettier": "^10.0.0",
"eslint-plugin-svelte": "^3.0.0",
"prettier": "^3.0.0",
"prettier-plugin-svelte": "^3.1.2",
"svelte": "^5.0.0",
@ -25,6 +25,6 @@
"dependencies": {
"bootstrap": "^5.2.2",
"bootstrap-icons": "^1.9.1",
"@sveltestrap/sveltestrap": "^6.2.1"
"@sveltestrap/sveltestrap": "^7.0.0"
}
}

View file

@ -9,12 +9,14 @@ import (
"log"
"net/http"
"strconv"
"srs.epita.fr/fic-server/libfic"
)
type AirbusAPI struct {
BaseURL string
Token string
SessionID int64
SessionUUID string
InsecureSkipVerify bool
}
@ -51,7 +53,7 @@ func (a *AirbusAPI) request(method, endpoint string, data io.Reader, out interfa
if out != nil {
jdec := json.NewDecoder(resp.Body)
if err := jdec.Decode(out); err != nil {
if err := jdec.Decode(&fic.CyberrangeAPIResponse{Data: out}); err != nil {
return fmt.Errorf("an error occurs when trying to decode response: %w", err)
}
}
@ -87,7 +89,7 @@ type AirbusUser struct {
}
func (a *AirbusAPI) GetUsers() (users AirbusUserData, err error) {
err = a.request("GET", fmt.Sprintf("/sessions/%d/users", a.SessionID), nil, &users)
err = a.request("GET", fmt.Sprintf("/sessions/%s/users", a.SessionUUID), nil, &users)
return
}
@ -137,7 +139,7 @@ type AirbusChallenge struct {
}
func (a *AirbusAPI) GetChallenges() (challenges AirbusChallengeData, err error) {
err = a.request("GET", fmt.Sprintf("/v1/sessions/%d/challenges", a.SessionID), nil, &challenges)
err = a.request("GET", fmt.Sprintf("/v1/sessions/%s/challenges", a.SessionUUID), nil, &challenges)
return
}
@ -156,25 +158,23 @@ func (a *AirbusAPI) GetChallengeFromName(name string) (*AirbusChallenge, error)
return nil, fmt.Errorf("unable to find challenge %q", name)
}
func (a *AirbusAPI) ValidateChallengeFromUser(team *AirbusTeam, challengeId AirbusChallengeId) (err error) {
log.Printf("ValidateChallenge: %d, %s, %d", a.SessionID, challengeId.String(), team.Members[0].ID)
func (a *AirbusAPI) ValidateChallengeFromUser(team *fic.CyberrangeTeam, challengeId AirbusChallengeId) (err error) {
log.Printf("ValidateChallenge: %s, %s, %s", a.SessionUUID, challengeId.String(), team.Members[0].UUID)
if dryRun {
return
}
err = a.request("GET", fmt.Sprintf("/v1/sessions/%d/%s/%d/validate", a.SessionID, challengeId.String(), team.Members[0].ID), nil, nil)
err = a.request("GET", fmt.Sprintf("/v1/sessions/%s/%s/%s/validate", a.SessionUUID, challengeId.String(), team.Members[0].UUID), nil, nil)
return
}
type AirbusUserAwards struct {
UserId int64 `json:"gaming_user_id"`
Message string `json:"name"`
Value int64 `json:"value"`
}
func (a *AirbusAPI) AwardUser(team *AirbusTeam, value int64, message string) (err error) {
func (a *AirbusAPI) AwardUser(team *fic.CyberrangeTeam, value int64, message string) (err error) {
awards := AirbusUserAwards{
UserId: team.Members[0].ID,
Message: message,
Value: value,
}
@ -190,7 +190,7 @@ func (a *AirbusAPI) AwardUser(team *AirbusTeam, value int64, message string) (er
return
}
err = a.request("POST", fmt.Sprintf("/v1/sessions/%d/awards", a.SessionID), bytes.NewReader(marshalled), nil)
err = a.request("POST", fmt.Sprintf("/v1/sessions/%s/%s/awards", a.SessionUUID, team.Members[0].UUID), bytes.NewReader(marshalled), nil)
if err != nil {
return err
}

View file

@ -16,6 +16,8 @@ import (
"time"
"gopkg.in/fsnotify.v1"
"srs.epita.fr/fic-server/libfic"
)
var (
@ -77,12 +79,8 @@ func main() {
fd.Close()
}
if v, exists := os.LookupEnv("AIRBUS_SESSIONID"); exists {
var err error
api.SessionID, err = strconv.ParseInt(v, 10, 64)
if err != nil {
log.Fatal("AIRBUS_SESSIONID is invalid: ", err.Error())
}
if v, exists := os.LookupEnv("AIRBUS_SESSION_UUID"); exists {
api.SessionUUID = v
} else if v, exists := os.LookupEnv("AIRBUS_SESSION_NAME_FILE"); exists {
fd, err := os.Open(v)
if err != nil {
@ -101,15 +99,15 @@ func main() {
for _, session := range sessions {
if session.Name == v {
api.SessionID = session.ID
api.SessionUUID = session.UUID
break
}
}
if api.SessionID == 0 {
if api.SessionUUID == "" {
log.Fatal("Session ID not found")
} else {
log.Println("Session ID discovered: ", api.SessionID)
log.Println("Session ID discovered: ", api.SessionUUID)
}
} else if v, exists := os.LookupEnv("AIRBUS_SESSION_NAME"); exists {
sessions, err := api.GetSessions()
@ -117,17 +115,19 @@ func main() {
log.Fatal("Unable to retrieve session: ", err)
}
found := false
for _, session := range sessions {
if session.Name == v {
api.SessionID = session.ID
api.SessionUUID = session.UUID
found = true
break
}
}
if api.SessionID == 0 {
if !found {
log.Fatal("Session ID not found")
} else {
log.Println("Session ID discovered: ", api.SessionID)
log.Println("Session ID discovered: ", api.SessionUUID)
}
} else {
sessions, err := api.GetSessions()
@ -138,7 +138,7 @@ func main() {
log.Println("Please define your AIRBUS_SESSIONID or AIRBUS_SESSION_NAME.")
log.Println("Existing sessions are:")
for _, session := range sessions {
log.Printf(" - %d: %q", session.ID, session.Name)
log.Printf(" - %s: %q", session.UUID, session.Name)
}
os.Exit(1)
}
@ -156,11 +156,11 @@ func main() {
}
fmt.Println("## Airbus' registered teams:")
fmt.Println("-------------------------------------------")
fmt.Println(" ID | Name | Nb. | Score | Rank")
fmt.Println("-------------------------------------------")
fmt.Println("----------------------------------------------------------------------------------")
fmt.Println(" UUID | Name | Nb. | Score | Rank")
fmt.Println("----------------------------------------------------------------------------------")
for _, team := range teams {
fmt.Printf("% 2d | % 15s | % 3d | % 5d | % 3d\n", team.ID, team.Name, len(team.Members), team.Score, team.Rank)
fmt.Printf(" %s | % 20s | % 3d | % 5d | % 3d\n", team.UUID, team.Name, len(team.Members), team.Score, team.Rank)
}
case "rank":
teams, err := api.GetTeams()
@ -169,7 +169,7 @@ func main() {
os.Exit(1)
}
ranking := []*AirbusTeam{}
ranking := []*fic.CyberrangeTeam{}
for _, team := range teams {
tmp := team
ranking = append(ranking, &tmp)
@ -178,11 +178,11 @@ func main() {
sort.Sort(sort.Reverse(ByScore(ranking)))
fmt.Println("## Airbus' ranking:")
fmt.Println("-------------------------------------")
fmt.Println(" Rank | Name | ID | Score")
fmt.Println("-------------------------------------")
fmt.Println("----------------------------------------------------------------------------")
fmt.Println(" Rank | Name | UUID | Score")
fmt.Println("----------------------------------------------------------------------------")
for _, team := range ranking {
fmt.Printf("% 5d | % 15s |% 3d | % 5d\n", team.Rank, team.Name, team.ID, team.Score)
fmt.Printf("% 5d | % 20s | %s | % 5d\n", team.Rank, team.Name, team.UUID, team.Score)
}
case "get":
teams, err := api.GetTeams()
@ -191,22 +191,18 @@ func main() {
os.Exit(1)
}
teamid, err := strconv.ParseInt(args[1], 10, 64)
if err != nil {
log.Println(err)
os.Exit(1)
}
teamid := args[1]
for _, team := range teams {
if team.ID == teamid {
fmt.Printf("## Airbus' registered team %d:\n\nID: %d\nName: %s\nScore: %d\nRank: %d\nMembers:\n", teamid, team.ID, team.Name, team.Score, team.Rank)
if team.UUID == teamid {
fmt.Printf("## Airbus' registered team %s:\n\nUUID: %s\nName: %s\nScore: %d\nRank: %d\nMembers:\n", teamid, team.UUID, team.Name, team.Score, team.Rank)
for _, member := range team.Members {
fmt.Printf(" - ID: %d\n Name: %s\n Nickname: %s\n E-mail: %s\n", member.ID, member.Name, member.Nickname, member.EMail)
fmt.Printf(" - UUID: %s\n Name: %s\n Nickname: %s\n E-mail: %s\n", member.UUID, member.Name, member.Nickname, member.EMail)
}
os.Exit(0)
}
}
fmt.Printf("Team %d not found. Use 'list' to view all existing teams\n", teamid)
fmt.Printf("Team %s not found. Use 'list' to view all existing teams\n", teamid)
case "award":
if len(args) < 3 {
fmt.Println("award <TEAM_ID> <VALUE> <MESSAGE>")
@ -219,11 +215,7 @@ func main() {
os.Exit(1)
}
teamid, err := strconv.ParseInt(args[1], 10, 64)
if err != nil {
log.Println("Invalid team id", err)
os.Exit(1)
}
teamid := args[1]
value, err := strconv.ParseInt(args[2], 10, 64)
if err != nil {
@ -232,7 +224,7 @@ func main() {
}
for _, team := range teams {
if team.ID == teamid {
if team.UUID == teamid {
err = api.AwardUser(&team, value, strings.Join(args[3:], " "))
if err != nil {
log.Println("Unable to award team:", err)
@ -243,7 +235,7 @@ func main() {
os.Exit(0)
}
}
fmt.Printf("Team %d not found. Use 'list' to view all existing teams\n", teamid)
fmt.Printf("Team %s not found. Use 'list' to view all existing teams\n", teamid)
}
os.Exit(0)

View file

@ -1,12 +1,18 @@
package main
import ()
import (
"time"
)
type Session struct {
Name string `json:"name"`
Status string `json:"status"`
ID int64 `json:"id"`
Mode string `json:"mode"`
Name string `json:"name"`
QuestionValidation string `json:"question_validation"`
Status string `json:"status"`
UUID string `json:"uuid"`
Mode string `json:"mode"`
Difficulty int `json:"difficulty"`
StartedAt time.Time `json:"start_at"`
FinishAt time.Time `json:"finish_at"`
}
func (a *AirbusAPI) GetSessions() (ret []Session, err error) {

View file

@ -2,44 +2,27 @@ package main
import (
"fmt"
"srs.epita.fr/fic-server/libfic"
)
type AirbusTeam struct {
ID int64 `json:"id"`
Members []TeamMember `json:"members"`
Name string `json:"name"`
Score int64 `json:"score"`
Rank int `json:"rank"`
}
type TeamMember struct {
ID int64 `json:"id"`
Name string `json:"name"`
Nickname string `json:"nickname"`
EMail string `json:"email"`
}
type airbusDataTeam struct {
Data []AirbusTeam `json:"data"`
}
func (a *AirbusAPI) GetTeams() ([]AirbusTeam, error) {
var data airbusDataTeam
err := a.request("GET", fmt.Sprintf("/v1/sessions/%d/teams", a.SessionID), nil, &data)
func (a *AirbusAPI) GetTeams() ([]fic.CyberrangeTeam, error) {
var data []fic.CyberrangeTeam
err := a.request("GET", fmt.Sprintf("/v1/sessions/%s/teams", a.SessionUUID), nil, &data)
if err != nil {
return nil, err
} else {
return data.Data, nil
return data, nil
}
}
type ByRank []*AirbusTeam
type ByRank []*fic.CyberrangeTeam
func (a ByRank) Len() int { return len(a) }
func (a ByRank) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByRank) Less(i, j int) bool { return a[i].Rank < a[j].Rank }
type ByScore []*AirbusTeam
type ByScore []*fic.CyberrangeTeam
func (a ByScore) Len() int { return len(a) }
func (a ByScore) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

View file

@ -1,102 +0,0 @@
[
{
"name": "#D\u00e9fendonsEnsemble",
"score": 0
},
{
"name": "Les Pires Hat",
"score": 0
},
{
"name": "Esnarcotrafiquants",
"score": 0
},
{
"name": "Quarkslab",
"score": 0
},
{
"name": "HackademINT",
"score": 0
},
{
"name": "Phreaks 2600",
"score": 0
},
{
"name": "Rhackgondins",
"score": 0
},
{
"name": "Team France",
"score": 0
},
{
"name": "Hack UTT",
"score": 0
},
{
"name": "HDRF",
"score": 0
},
{
"name": "#199",
"score": 0
},
{
"name": "Hackday",
"score": 0
},
{
"name": "GCC",
"score": 0
},
{
"name": "Apeltek",
"score": 0
},
{
"name": "Hackvengers",
"score": 0
},
{
"name": "Guardia Cyber Squad",
"score": 0
},
{
"name": "F0r3nS0C",
"score": 0
},
{
"name": "Oteriack",
"score": 0
},
{
"name": "Cervelles de Canuts",
"score": 0
},
{
"name": "Next'H4ck",
"score": 0
},
{
"name": "Overflowl",
"score": 0
},
{
"name": "Capgemini Aces of Spades",
"score": 0
},
{
"name": "DLS",
"score": 0
},
{
"name": "ESGI",
"score": 0
},
{
"name": "0xECE",
"score": 0
}
]

View file

@ -5,6 +5,8 @@ import (
"log"
"os"
"time"
"srs.epita.fr/fic-server/libfic"
)
type TSValue struct {
@ -32,7 +34,7 @@ func loadTS(tspath string) (timestamp map[string]*TSValue, err error) {
}
}
func loadTSFromAPI(teams map[string]*AirbusTeam) (timestamp map[string]*TSValue, err error) {
func loadTSFromAPI(teams map[string]*fic.CyberrangeTeam) (timestamp map[string]*TSValue, err error) {
now := time.Now()
timestamp = map[string]*TSValue{}

View file

@ -23,7 +23,7 @@ type Walker struct {
Exercices AirbusExercicesBindings
Teams map[string]fic.ExportedTeam
RevTeams map[string]string
TeamBindings map[string]*AirbusTeam
TeamBindings map[string]*fic.CyberrangeTeam
API AirbusAPI
Coeff float64
}
@ -35,11 +35,11 @@ func (w *Walker) fetchTeams() error {
}
w.RevTeams = map[string]string{}
w.TeamBindings = map[string]*AirbusTeam{}
w.TeamBindings = map[string]*fic.CyberrangeTeam{}
for tid, team := range w.Teams {
for i, t := range teams {
if team.Name == t.Name || team.ExternalId == t.Name {
if team.Name == t.Name || team.ExternalId == t.Name || team.ExternalId == t.UUID {
w.TeamBindings[tid] = &teams[i]
break
}
@ -143,7 +143,7 @@ func (w *Walker) WalkScore(path string, d os.DirEntry, err error) error {
return nil
}
func (w *Walker) TreatScoreGrid(path string, airbusTeam *AirbusTeam) error {
func (w *Walker) TreatScoreGrid(path string, airbusTeam *fic.CyberrangeTeam) error {
// Read score grid
fdscores, err := os.Open(path)
if err != nil {