Ανάλυση σύνταξης και επικύρωση εισόδου — DedSec Project
Ανάλυση σύνταξης και επικύρωση — περιορισμένη μορφή ρυθμίσεων Πρόβλημα και συμβόλαιο εισόδου Μία τοπική εφαρμογή CLI διαβάζει μικρό αρχείο ρυθμίσεων με mode, limit και label. Το περιεχόμενο…
Ανάλυση σύνταξης και επικύρωση — περιορισμένη μορφή ρυθμίσεων
Πρόβλημα και συμβόλαιο εισόδου
Μία τοπική εφαρμογή CLI διαβάζει μικρό αρχείο ρυθμίσεων με mode, limit και label. Το περιεχόμενο θεωρείται μη έμπιστο κείμενο: δεν πρέπει να εκτελείται ως κώδικας Python, κέλυφος, pickle ή πρότυπο. Κάθε επιτρεπόμενο κλειδί εμφανίζεται μόνο μία φορά. Η μόνη λειτουργία είναι read, το όριο πρέπει να είναι ακέραιος ASCII από 1 έως 50, και η ετικέτα δεν επιτρέπεται να είναι κενή. Κενές γραμμές και σχόλια ολόκληρης γραμμής είναι έγκυρα. Άγνωστα κλειδιά, επαναλήψεις, υπερβολικό μέγεθος UTF-8 και λανθασμένα ζεύγη πρέπει να απορρίπτονται. Η απλή μορφή δεν περιλαμβάνει εμφωλευμένες δομές, πολυγραμμικές τιμές σε εισαγωγικά ή σχόλια στην ίδια γραμμή.
Εκτελέσιμο εργαστήριο Python 3
Αποθήκευσε ως config_lab.py και εκτέλεσε python3 config_lab.py. Ο αναλυτής δέχεται κείμενο και δεν διαβάζει προσωπικά αρχεία: όλες οι δοκιμές χρησιμοποιούν τεχνητά δεδομένα.
import re
def parse_config(text):
if len(text.encode('utf-8')) > 2048:
raise ValueError('configuration too large')
allowed = {'mode', 'limit', 'label'}
result = {}
for number, line in enumerate(text.splitlines(), 1):
stripped = line.strip()
if not stripped or stripped.startswith('#'):
continue
key, sep, value = stripped.partition('=')
key = key.strip()
value = value.strip()
if not sep or not re.fullmatch(r'[a-z]+', key) or key not in allowed:
raise ValueError(f'line {number}: bad or unknown key')
if key in result:
raise ValueError(f'line {number}: duplicate {key}')
result[key] = value
if result.get('mode') != 'read':
raise ValueError('mode must be read')
limit = result.get('limit')
if limit is None or not re.fullmatch(r'[0-9]+', limit) or not 1 <= int(limit) <= 50:
raise ValueError('limit must be integer 1..50')
if not result.get('label'):
raise ValueError('label must be nonempty')
return {'mode': 'read', 'limit': int(limit), 'label': result['label']}
assert parse_config('# comment\nmode=read\nlimit=5\nlabel=Ελληνικά\n')['label'] == 'Ελληνικά'
for bad in ('mode=write\nlimit=1\nlabel=x', 'mode=read\nlimit=0\nlabel=x',
'mode=read\nlimit=2\nlabel=', 'mode=read\nlimit=2\nlabel=x\nlimit=3',
'mode=read\nlimit=2\nlabel=x\nexec=yes',
'mode=read\nlimit=2\nlabel=x\n' + 'x'*2048):
try: parse_config(bad)
except ValueError: pass
else: raise AssertionError('bad input accepted')
print('PASS parser: schema, Unicode, repeated keys, limits and size')
Διαχωρισμός σύνταξης από κανόνες
Η partition εντοπίζει το πρώτο ίσον, αλλά αυτό από μόνο του δεν σημαίνει ότι ένα κλειδί επιτρέπεται. Το κανονικό μοτίβο δέχεται μόνο πεζά λατινικά γράμματα, ενώ μία ρητή λίστα απορρίπτει απροσδόκητα ονόματα. Ο έλεγχος διπλοτύπων αποτρέπει τη σιωπηρή αντικατάσταση προηγούμενης τιμής. Για το όριο προηγείται ο συντακτικός έλεγχος και ακολουθεί η μετατροπή σε ακέραιο και ο έλεγχος του διαστήματος. Η απουσία, η κενή τιμή και η λανθασμένη μορφή αποτελούν διαφορετικές περιπτώσεις, παρότι εδώ έχουν κοινή δημόσια κατηγορία σφάλματος. Το μέγεθος ελέγχεται σε byte UTF-8 πριν από την ανάλυση, όχι μόνο σε χαρακτήρες. Αν η γλώσσα ρυθμίσεων επεκταθεί, πρόσθεσε όρια αριθμού και μήκους γραμμών. Ποτέ μη συνθέτεις εντολές κελύφους από τέτοιο κείμενο.
Ασκήσεις και εντοπισμός αποτυχιών
- Επανάλαβε το ίδιο κλειδί δύο φορές. Ο έλεγχος πρέπει να σταματά στη δεύτερη εμφάνιση και να μην κρατά απλώς την τελευταία τιμή.
- Δοκίμασε 0, 005, 51, -1, 1.5 και αραβικούς αριθμητικούς χαρακτήρες. Εξήγησε την επιλογή μόνο ASCII.
- Πρόσθεσε άγνωστο κλειδί exec και επιβεβαίωσε ότι δεν αντιμετωπίζεται ως εντολή. Δοκίμασε ίσον μέσα σε τιμή και όρισε κανόνα διαφυγής.
- Δημιούργησε μεγάλο ελληνικό κείμενο και σύγκρινε byte με χαρακτήρες. Πρόσθεσε διαφορετικά διαγνωστικά για απούσα και κενή υποχρεωτική τιμή.
Παραδοτέα: PASS, αρνητικές δοκιμές, περιγραφή γραμματικής και καταγραφή της σύνταξης που δεν υποστηρίζεται.