@@ -10,6 +10,8 @@ import { readFixes } from '../routes/vulnCodeFixes'
10
10
import { type Challenge } from '../data/types'
11
11
import { getCodeChallenges } from './codingChallenges'
12
12
import logger from './logger'
13
+ import { type NextFunction , type Request , type Response } from 'express'
14
+ import * as utils from './utils'
13
15
const median = require ( 'median' )
14
16
15
17
const coupledChallenges = { // TODO prevent also near-identical challenges (e.g. all null byte file access or dom xss + bonus payload etc.) from counting as cheating
@@ -23,6 +25,25 @@ const trivialChallenges = ['errorHandlingChallenge', 'privacyPolicyChallenge', '
23
25
24
26
const solves : Array < { challenge : any , phase : string , timestamp : Date , cheatScore : number } > = [ { challenge : { } , phase : 'server start' , timestamp : new Date ( ) , cheatScore : 0 } ] // seed with server start timestamp
25
27
28
+ const preSolveInteractions : Array < { challengeKey : any , urlFragments : string [ ] , interactions : number } > = [
29
+ {
30
+ challengeKey : 'missingEncodingChallenge' ,
31
+ urlFragments : [ '/assets/public/images/uploads/%F0%9F%98%BC-' ] ,
32
+ interactions : 0
33
+ }
34
+ ]
35
+
36
+ exports . checkForPreSolveInteractions = ( ) => ( { url } : Request , res : Response , next : NextFunction ) => {
37
+ preSolveInteractions . forEach ( ( preSolveInteraction ) => {
38
+ preSolveInteraction . urlFragments . forEach ( ( urlFragment ) => {
39
+ if ( utils . endsWith ( url , urlFragment ) ) {
40
+ preSolveInteraction . interactions ++
41
+ }
42
+ } )
43
+ } )
44
+ next ( )
45
+ }
46
+
26
47
export const calculateCheatScore = ( challenge : Challenge ) => {
27
48
const timestamp = new Date ( )
28
49
let cheatScore = 0
@@ -37,7 +58,14 @@ export const calculateCheatScore = (challenge: Challenge) => {
37
58
const minutesSincePreviousSolve = ( timestamp . getTime ( ) - previous ( ) . timestamp . getTime ( ) ) / 60000
38
59
cheatScore += Math . max ( 0 , 1 - ( minutesSincePreviousSolve / minutesExpectedToSolve ) )
39
60
40
- logger . info ( `Cheat score for ${ areCoupled ( challenge , previous ( ) . challenge ) ? 'coupled ' : ( isTrivial ( challenge ) ? 'trivial ' : '' ) } ${ challenge . tutorialOrder ? 'tutorial ' : '' } ${ colors . cyan ( challenge . key ) } solved in ${ Math . round ( minutesSincePreviousSolve ) } min (expected ~${ minutesExpectedToSolve } min) with${ config . get ( 'challenges.showHints' ) ? '' : 'out' } hints allowed: ${ cheatScore < 0.33 ? colors . green ( cheatScore . toString ( ) ) : ( cheatScore < 0.66 ? colors . yellow ( cheatScore . toString ( ) ) : colors . red ( cheatScore . toString ( ) ) ) } ` )
61
+ const preSolveInteraction = preSolveInteractions . find ( ( preSolveInteraction ) => preSolveInteraction . challengeKey === challenge . key )
62
+ let percentPrecedingInteraction = - 1
63
+ if ( preSolveInteraction ) {
64
+ percentPrecedingInteraction = preSolveInteraction . interactions / ( preSolveInteraction . urlFragments . length * challenge . difficulty )
65
+ // TODO Add impact on actual cheat score
66
+ }
67
+
68
+ logger . info ( `Cheat score for ${ areCoupled ( challenge , previous ( ) . challenge ) ? 'coupled ' : ( isTrivial ( challenge ) ? 'trivial ' : '' ) } ${ challenge . tutorialOrder ? 'tutorial ' : '' } ${ colors . cyan ( challenge . key ) } solved in ${ Math . round ( minutesSincePreviousSolve ) } min (expected ~${ minutesExpectedToSolve } min) with${ config . get ( 'challenges.showHints' ) ? '' : 'out' } hints allowed${ percentPrecedingInteraction > - 1 ? ( ' and ' + percentPrecedingInteraction * 100 + '% expected preceding URL interaction' ) : '' } : ${ cheatScore < 0.33 ? colors . green ( cheatScore . toString ( ) ) : ( cheatScore < 0.66 ? colors . yellow ( cheatScore . toString ( ) ) : colors . red ( cheatScore . toString ( ) ) ) } ` )
41
69
solves . push ( { challenge, phase : 'hack it' , timestamp, cheatScore } )
42
70
return cheatScore
43
71
}
0 commit comments