Skip to content

Commit 7b1d704

Browse files
vchrombieHooshangi
andcommitted
Add auto_grader.py script
This commit adds the automated grading script for repy_v2 from https://github.com/Hooshangi/Grading-script. It also adds some documentation. Co-authored-by: Sara Hooshangi <[email protected]>
1 parent 0c9ae5e commit 7b1d704

File tree

2 files changed

+302
-0
lines changed

2 files changed

+302
-0
lines changed

Scripts/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# `auto_grader.py`
2+
> **Automated Grading for Repy V2 Assignments**
3+
4+
`auto_grader.py` is a Python-based tool designed to streamline the grading
5+
process for defense and attack programs written in [Repy
6+
V2](https://github.com/SeattleTestbed/repy_v2). By automatically running each
7+
attack against every defense, it produces comprehensive results in a CSV format.
8+
9+
## Description
10+
With `auto_grader.py`, instructors can effortlessly assess the efficacy of
11+
students' defense mechanisms in the face of potential attacks. For every attack
12+
that succeeds in compromising a defense, the resulting CSV will register a `1`;
13+
otherwise, it will display a `0`. An attack is considered successful if it
14+
generates an output or raises an error, denoting the failure of the defense
15+
layer. It also handles timeouts, ensuring that the script does not hang in the
16+
event of an infinite loop.
17+
18+
## Prerequisites
19+
- Python 2.7 installed on your machine.
20+
- Repy V2 environment setup.
21+
- Copy the required files (mentioned below) in the script's directory
22+
- `repy.py`
23+
- `restrictions.default`
24+
- `wrapper.r2py`
25+
- `encasementlib.r2py`
26+
27+
## Usage
28+
```bash
29+
python auto_grader.py defense_folder_path attack_folder_path temp_target_folder_path
30+
```
31+
where:
32+
- `defense_folder_path` is the path to the folder containing defense programs.
33+
- `attack_folder_path` is the path to the folder containing attack programs.
34+
- `temp_target_folder_path` is the path to the temporary target folder.
35+
36+
## Naming Conventions
37+
- Attack programs should be named as: "`[studentid]_attackcase[number].r2py`".
38+
- Defense programs should start with the name
39+
"`reference_monitor_[studentid].r2py`".
40+
41+
For example, if the student id is `abc123`, the defense program should be named
42+
as `reference_monitor_abc123.r2py` and the attack program should be named as
43+
`abc123_attackcase1.r2py`, `abc123_attackcase2.r2py`, etc.
44+
45+
## Output
46+
Two CSV files are generated:
47+
1. `All_Attacks_matrix.csv`: Contains the result of every attack program against
48+
each defense.
49+
2. `All_Students_matrix.csv`: Indicates which students successfully attacked a
50+
defense.
51+
52+
## Notes
53+
- Students are instructed to generate output or raise an error in their attack
54+
program only when they successfully compromise the security layer.
55+
- Ensure the correct environment, naming conventions, and directory structures
56+
are adhered to for successful script execution.
57+
58+
## Contributing
59+
For modifications, improvements, or any issues, please open a pull request or
60+
issue.
61+
62+
## Credits
63+
`auto_grader.py` is the brainchild of
64+
[@Hooshangi](https://github.com/Hooshangi).
65+
66+
For further details, potential contributions, or to view the code, visit
67+
[Hooshangi/Grading-script](https://github.com/Hooshangi/Grading-script).

Scripts/auto_grader.py

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
"""
2+
<Program Name>
3+
auto_grader.py
4+
5+
<Purpose>
6+
Module which takes all defense programs and attack programs in Repy V2 and creates
7+
csv file describing test result of every attack program against each defense program.
8+
Under the attack program column, it shows one if attack was able to compromise security layer
9+
and 0 otherwise.
10+
Students submitting attack and defense programs are instructed to log output or raise error
11+
in their attack program only when the security layer gets failed. In this program we check which
12+
attack program is producing output or error. If so that means the attack is successful and that
13+
security layer is compromised.
14+
15+
<usage>
16+
-All defense programs should be in a folder and all attack programs in another folder.
17+
auto_grader, repy.py ,restrictions.default, wrapper.r2py, encasementlib.r2py.
18+
-Create a temporary target folder inside the directory.
19+
20+
python auto_grader.py defense_folder_path attack_folder_path temp_target_folder_path
21+
22+
"""
23+
24+
25+
26+
27+
import os
28+
import glob
29+
import subprocess
30+
import shutil
31+
import csv
32+
import time
33+
import signal # using SIGKILL
34+
import sys
35+
36+
37+
path_DefenseFolder = sys.argv[1]
38+
path_AttackFolder = sys.argv[2]
39+
40+
41+
# Paths to temp folder where the attack is run
42+
path_TempFolder = sys.argv[3]
43+
44+
#part of filename to look for
45+
attack_ext = "*.r2py" #extension of attack programs will be r2py
46+
def_ext = "reference*" #students are instructed to name their defense program starting with reference
47+
48+
49+
50+
51+
def get_student_ID(attack_fnlist):
52+
''' Given a list of the total attack files, get a list of all of the
53+
student IDs. This assumes that the file names contains _
54+
'''
55+
56+
studentset = set()
57+
for attackfn in attack_fnlist:
58+
# I assume everything before the first _ is the student ID. I will
59+
# raise an exception for a file like foo.repy...
60+
61+
if len(attackfn.split('_')) == 0:
62+
raise ValueError('File name "'+attackfn+'" should contain an underscore!')
63+
64+
studentname = attackfn.split('_')[0]
65+
studentset.add(studentname)
66+
67+
return sorted(list(studentset))
68+
69+
70+
def get_student_attack_code(studentname, attack_fnlist):
71+
''' This will return only the attack code from a specific student.'''
72+
73+
thisstudentattacks = []
74+
for thisattack in attack_fnlist:
75+
# need the underscore to stop bob from matching bobby's tests.
76+
if thisattack.startswith(studentname+'_'):
77+
thisstudentattacks.append(thisattack)
78+
79+
80+
return thisstudentattacks
81+
82+
83+
def check_if_student_attacks_succeed(student_attackfn, defensefn):
84+
''' Returns a list of any code for a student that produce stderr or stdout.
85+
An empty list is returned if none were successful
86+
otherwise'''
87+
88+
successfulattacks = []
89+
90+
for attackfn in student_attackfn:
91+
if did_this_attack_succeed(attackfn, defensefn):
92+
successfulattacks.append(attackfn)
93+
94+
return successfulattacks
95+
96+
97+
98+
99+
def did_this_attack_succeed(attackFilename, defenseFilename):
100+
''' Returns True if the attack produces stderr or stdout,
101+
False otherwise
102+
'''
103+
104+
timeout=30
105+
106+
os.mkdir(path_TempFolder) # make a temp folder
107+
os.chdir(path_TempFolder) # cd to temp folder at this point
108+
109+
shutil.copy(path_DefenseFolder + '/' + defenseFilename, path_TempFolder + '/' + defenseFilename)
110+
shutil.copy(path_AttackFolder + '/' + attackFilename, path_TempFolder + '/' + attackFilename)
111+
shutil.copy('../wrapper.r2py', path_TempFolder + '/wrapper.r2py')
112+
start = time.time()
113+
114+
pobj = subprocess.Popen(
115+
['python', '../repy.py', '--stop=Repy_stop_this', '../restrictions.default', '../encasementlib.r2py', defenseFilename,
116+
attackFilename], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
117+
118+
# NOT A BUG: Note that this will not get all of the stdout or stderr because we are polling for completion.
119+
# Substantial output may cause the program to block, which is a very bad thing... in most cases.
120+
# Since output / errput is failure and timeout is failure, we're actually okay with it here.
121+
while pobj.poll() is None:
122+
time.sleep(0.1)
123+
now = time.time()
124+
if now - start > timeout:
125+
# Signal for the repyVM to stop (due to the --stop option)
126+
file("Repy_stop_this","w").close()
127+
# wait for it to stop...
128+
pobj.wait()
129+
stdout = "timeout"
130+
stderr = "timeout"
131+
break
132+
else:
133+
(stdout, stderr) = pobj.communicate()
134+
135+
136+
os.chdir(path_AttackFolder) # go back to attack folder
137+
shutil.rmtree(path_TempFolder) #remove the temp directory
138+
139+
140+
if stdout != '' or stderr !='':
141+
return True
142+
else:
143+
return False
144+
145+
146+
147+
def row_builder(success_list,original_list,defenseFilename):
148+
149+
num_success=len(success_list)
150+
151+
matrix_row=[0]*(len(original_list))
152+
matrix_row[0]=defenseFilename
153+
154+
for element in range(num_success):
155+
156+
for row_num in range(len(original_list)):
157+
if original_list[row_num]==success_list[element]:
158+
matrix_row[row_num]=1 # if an attack is successful, put a 1 in the matrix
159+
160+
return matrix_row
161+
162+
163+
164+
165+
166+
167+
def main():
168+
169+
# If the temp folder is left over from last time, remove it.
170+
if os.path.exists(path_TempFolder):
171+
shutil.rmtree(path_TempFolder)
172+
173+
os.chdir(path_DefenseFolder) # cd to defense monitor folder
174+
defense_fnlist =glob.glob(def_ext)
175+
176+
os.chdir(path_AttackFolder) # cd to attack folder
177+
attack_fnlist=glob.glob(attack_ext)
178+
179+
studentIDlist = get_student_ID(attack_fnlist)
180+
print 'number of students in the course', len(studentIDlist)
181+
182+
header_attackmatrix=attack_fnlist
183+
header_attackmatrix.insert(0,'All attack files-->')
184+
185+
header_studentmatrix=studentIDlist
186+
header_studentmatrix.insert(0,'All students -->')
187+
188+
resultFile1 = open("All_Attacks_matrix.csv",'wb')
189+
wr_allattacks = csv.writer(resultFile1)
190+
wr_allattacks.writerow(header_attackmatrix)
191+
192+
resultFile2 = open("All_Students_matrix.csv",'wb')
193+
wr_students = csv.writer(resultFile2)
194+
wr_students.writerow(header_studentmatrix)
195+
196+
197+
for defenseFilename in defense_fnlist:
198+
collection_successful_attacks=list()
199+
collection_successful_students=list()
200+
201+
print 'This is defense file --->', defenseFilename
202+
203+
for attackingstudent in studentIDlist:
204+
205+
# get just this student's attacks
206+
student_attackfns = get_student_attack_code(attackingstudent,attack_fnlist)
207+
successfulattacks = check_if_student_attacks_succeed(student_attackfns,defenseFilename)
208+
209+
210+
if successfulattacks!=[]:
211+
212+
print defenseFilename,'---attacked by--- ', attackingstudent,'----->', successfulattacks
213+
collection_successful_attacks=collection_successful_attacks+successfulattacks
214+
collection_successful_students.append(attackingstudent)
215+
216+
#print 'successful attacks',collection_successful_attacks
217+
row_all_attacks=row_builder(collection_successful_attacks,attack_fnlist,defenseFilename)
218+
row_students=row_builder(collection_successful_students,studentIDlist,defenseFilename)
219+
220+
wr_allattacks.writerow(row_all_attacks)
221+
wr_students.writerow(row_students)
222+
223+
print 'row completed with students',row_students
224+
225+
226+
227+
228+
229+
if __name__ == "__main__":
230+
main()
231+
232+
233+
234+
235+

0 commit comments

Comments
 (0)