1
- import Util from "../util " ;
1
+ import { isRealJSONParse } from "./is-real-json-parse " ;
2
2
3
3
import type { PerseusItem } from "../perseus-types" ;
4
4
5
- const deepEq = Util . deepEq ;
6
-
7
5
/**
8
6
* Helper to parse PerseusItem JSON
9
7
* Why not just use JSON.parse? We want:
@@ -13,90 +11,10 @@ const deepEq = Util.deepEq;
13
11
* @returns {PerseusItem } the parsed PerseusItem object
14
12
*/
15
13
export function parsePerseusItem ( json : string ) : PerseusItem {
16
- const randomPhrase = buildRandomPhrase ( ) ;
17
- const randomHintPhrase = buildRandomPhrase ( ) ;
18
- const randomString = buildRandomString ( ) ;
19
- const testingObject = JSON . stringify ( {
20
- answerArea : {
21
- calculator : false ,
22
- chi2Table : false ,
23
- financialCalculatorMonthlyPayment : false ,
24
- financialCalculatorTimeToPayOff : false ,
25
- financialCalculatorTotalAmount : false ,
26
- periodicTable : false ,
27
- periodicTableWithKey : false ,
28
- tTable : false ,
29
- zTable : false ,
30
- } ,
31
- hints : [ randomHintPhrase , `=${ Math . floor ( Math . random ( ) * 50 ) + 1 } ` ] ,
32
- itemDataVersion : { major : 0 , minor : 1 } ,
33
- question : {
34
- content : `${ randomPhrase } ` ,
35
- images : { } ,
36
- widgets : {
37
- expression1 : {
38
- alignment : "default" ,
39
- graded : false ,
40
- options : {
41
- answerForms : [
42
- {
43
- considered : "wrong" ,
44
- form : false ,
45
- key : 0 ,
46
- simplify : false ,
47
- value : `${ randomString } ` ,
48
- } ,
49
- ] ,
50
- ariaLabel : "Answer" ,
51
- buttonSets : [ "basic" ] ,
52
- functions : [ "f" , "g" , "h" ] ,
53
- static : true ,
54
- times : false ,
55
- visibleLabel : "Answer" ,
56
- } ,
57
- static : true ,
58
- type : "expression" ,
59
- version : { major : 1 , minor : 0 } ,
60
- } ,
61
- } ,
62
- } ,
63
- } ) ;
64
- // @ts -expect-error TS2550: Property 'replaceAll' does not exist on type 'string'.
65
- const testJSON = buildTestData ( testingObject . replaceAll ( '"' , '\\"' ) ) ;
66
- const parsedJSON = JSON . parse ( testJSON ) ;
67
- const parsedItemData : string = parsedJSON . data . assessmentItem . item . itemData ;
68
- const isNotCheating = deepEq ( parsedItemData , testingObject ) ;
69
- if ( isNotCheating ) {
14
+ // Try to block a cheating vector which relies on monkey-patching
15
+ // JSON.parse
16
+ if ( isRealJSONParse ( JSON . parse ) ) {
70
17
return JSON . parse ( json ) ;
71
18
}
72
19
throw new Error ( "Something went wrong." ) ;
73
20
}
74
-
75
- function buildRandomString ( capitalize : boolean = false ) {
76
- let randomString : string = "" ;
77
- const randomLength = Math . floor ( Math . random ( ) * 8 ) + 3 ;
78
- for ( let i = 0 ; i < randomLength ; i ++ ) {
79
- const randomLetter = String . fromCharCode (
80
- 97 + Math . floor ( Math . random ( ) * 26 ) ,
81
- ) ;
82
- randomString +=
83
- capitalize && i === 0 ? randomLetter . toUpperCase ( ) : randomLetter ;
84
- }
85
- return randomString ;
86
- }
87
-
88
- function buildRandomPhrase ( ) {
89
- const phrases : string [ ] = [ ] ;
90
- const randomLength = Math . floor ( Math . random ( ) * 10 ) + 5 ;
91
- for ( let i = 0 ; i < randomLength ; i ++ ) {
92
- phrases . push ( buildRandomString ( i === 0 ) ) ;
93
- }
94
- const modifierStart = [ "**" , "$" ] ;
95
- const modifierEnd = [ "**" , "$" ] ;
96
- const modifierIndex = Math . floor ( Math . random ( ) * modifierStart . length ) ;
97
- return `${ modifierStart [ modifierIndex ] } ${ phrases . join ( " " ) } ${ modifierEnd [ modifierIndex ] } ` ;
98
- }
99
-
100
- function buildTestData ( testObject : string ) {
101
- return `{"data":{"assessmentItem":{"__typename":"AssessmentItemOrError","error":null,"item":{"__typename":"AssessmentItem","id":"x890b3c70f3e8f4a6","itemData":"${ testObject } ","problemType":"Type 1","sha":"c7284a3ad65214b4e62bccce236d92f7f5d35941"}}}}` ;
102
- }
0 commit comments