Module M.05
Missing Access Control
The Open Door
A public write method without access control is like a bank vault with no door. Anyone on the network can call it and modify state. In GenLayer, always validate the sender address against an owner or authorized roles before allowing state mutations.
Side-by-side · Vulnerable vs. Patched
two contracts · proven by paired transactions
vulnerable ▸contracts/vulnerable/VulnerableVault.py
Failed TX> consensus failed · validators diverged
1# { "Depends": "py-genlayer:15qfivjvy80800rh998pcxmd2m8va1wq2qzqhz850n8ggcr4i9q0" }23from genlayer import *4import json56# Module 5 (Vulnerable) -- Missing access control.7# `mint` is annotated @gl.public.write but performs no sender check.8# Any address on the network can mint tokens to themselves indefinitely.9# Deployment succeeds; the bug is observable at call-time.101112class VulnerableVault(gl.Contract):13 balances_json: str14 total_supply: str1516 def __init__(self):17 self.balances_json = "{}"18 self.total_supply = "0"1920 @gl.public.write21 def mint(self, amount: int) -> None:22 # BUG: no auth.23 addr = str(gl.message.sender_address)24 balances = json.loads(self.balances_json)25 balances[addr] = int(balances.get(addr, 0)) + int(amount)26 self.balances_json = json.dumps(balances)27 self.total_supply = str(int(self.total_supply) + int(amount))2829 @gl.public.view30 def balance_of(self, addr: str) -> str:31 balances = json.loads(self.balances_json)32 return str(int(balances.get(addr, 0)))3334 @gl.public.view35 def get_total_supply(self) -> str:36 return self.total_supply
patched ▸contracts/patched/SecureVault.py
Success TX> consensus reached · all validators agree
1# { "Depends": "py-genlayer:15qfivjvy80800rh998pcxmd2m8va1wq2qzqhz850n8ggcr4i9q0" }23from genlayer import *4import json56# Module 5 (Patched) -- Role-based access control.7# Owner is set in the constructor from the deployer's address; only the8# owner can grant/revoke minter rights; only minters can mint.91011class SecureVault(gl.Contract):12 owner: str13 minters_json: str # {"0xabc...": true, ...}14 balances_json: str15 total_supply: str1617 def __init__(self):18 deployer = str(gl.message.sender_address)19 self.owner = deployer20 self.minters_json = json.dumps({deployer: True})21 self.balances_json = "{}"22 self.total_supply = "0"2324 def _require_owner(self) -> None:25 if str(gl.message.sender_address) != self.owner:26 raise Exception("only owner")2728 def _require_minter(self) -> None:29 minters = json.loads(self.minters_json)30 if not minters.get(str(gl.message.sender_address), False):31 raise Exception("only minter")3233 @gl.public.write34 def add_minter(self, addr: str) -> None:35 self._require_owner()36 minters = json.loads(self.minters_json)37 minters[addr] = True38 self.minters_json = json.dumps(minters)3940 @gl.public.write41 def remove_minter(self, addr: str) -> None:42 self._require_owner()43 minters = json.loads(self.minters_json)44 minters[addr] = False45 self.minters_json = json.dumps(minters)4647 @gl.public.write48 def mint(self, amount: int) -> None:49 self._require_minter()50 addr = str(gl.message.sender_address)51 balances = json.loads(self.balances_json)52 balances[addr] = int(balances.get(addr, 0)) + int(amount)53 self.balances_json = json.dumps(balances)54 self.total_supply = str(int(self.total_supply) + int(amount))5556 @gl.public.view57 def balance_of(self, addr: str) -> str:58 balances = json.loads(self.balances_json)59 return str(int(balances.get(addr, 0)))6061 @gl.public.view62 def is_minter(self, addr: str) -> str:63 minters = json.loads(self.minters_json)64 return "true" if minters.get(addr, False) else "false"6566 @gl.public.view67 def get_total_supply(self) -> str:68 return self.total_supply
Call invoked
mint(1000)no sender check -> any wallet can mint freely
Call invoked
mint(1000)deployer is owner+minter -> succeeds for the right caller
On-chain receipts
Knowledge check · M.05
01 / 02
Two questions on this incident. Pick the best answer; the question locks once committed.
Question 01 / 02
What happens if a @gl.public.write method has no access control?