Release Bludit Secure 0.1.5

This commit is contained in:
interlope committed 2026-11-02 20:50:15 +01:00
1 parent 49b4308028
commit 0b983bcfbb
10 files changed
+320 -154

No files matched your search

+7 -4
View File
@@ -15,15 +15,18 @@ keyfile=str(pathlib.Path(FIXTURE)/'test-key.pem')
raw=subprocess.check_output(PHP_CMD+['-r',f'$k=openssl_pkey_new(["private_key_type"=>OPENSSL_KEYTYPE_EC,"curve_name"=>"prime256v1"]);openssl_pkey_export($k,$pem);file_put_contents("{keyfile}",$pem);$d=openssl_pkey_get_details($k);echo json_encode([base64_encode($d["ec"]["x"]),base64_encode($d["ec"]["y"])]);'])
x,y=map(base64.b64decode,json.loads(raw))
s,b,h=req();token=csrf(b)
s,b,h=req('/admin/login?bs=password',{'secureCsrf':token,'username':'admin','password':'TestPassword123'});s,b,h=req('/admin/login?bs=account');token=csrf(b)
s,b,h=req('/admin/login?bs=password',{'secureCsrf':token,'username':'admin','password':'TestPassword123'});s,b,h=req('/admin/plugin/pluginbluditsecure');token=csrf(b)
def post(action,**fields):return req('/admin/login?bs='+action,{'secureCsrf':token,**fields})
s,b,h=post('key-create-options');options=json.loads(b);check(s==200,'registration options')
cred=b'bludit-test-key-1';rp=hashlib.sha256(b'127.0.0.1').digest()
auth=rp+b'\x45'+struct.pack('>I',0)+b'\0'*16+struct.pack('>H',len(cred))+cred+cbor({1:2,3:-7,-1:1,-2:x,-3:y})
client=json.dumps({'type':'webauthn.create','challenge':options['publicKey']['challenge'],'origin':'https://127.0.0.1:18765'}).encode()
s,b,h=post('key-create-finish',clientDataJSON=b64(client),attestationObject=b64(cbor({'fmt':'none','attStmt':{},'authData':auth})),label='Test passkey');check(s==200,'signed registration accepted')
s,b,h=req('/admin/login?bs=account');check('Test passkey' in b,'registered passkey listed');token=csrf(b)
post('logout');s,b,h=req();token=csrf(b);post('password',username='admin',password='TestPassword123');s,b,h=req('/admin/');token=csrf(b);check('Vérification en deux étapes' in b,'passkey enabled as second factor')
s,b,h=req('/admin/plugin/pluginbluditsecure');check('Test passkey' in b,'registered passkey listed');token=csrf(b)
post('logout');s,b,h=req();token=csrf(b);post('password',username='admin',password='TestPassword123');s,b,h=req('/admin/');token=csrf(b);check('Two-step verification' in b and 'login-card' in b,'MFA uses native login layout')
check('data-auto-passkey="true"' in b,'passkey prompt starts automatically')
check(re.search(r'assets/secure\.js\?v=[0-9a-f]{16}', b), 'login script URL has a content hash to invalidate stale caches')
check('name="password"' not in b and 'assets/secure.css' not in b,'native password form replaced only during second phase')
def assertion(origin,valid=True):
s,b,h=post('key-get-options');check(s==200,'assertion options');opts=json.loads(b)
client=json.dumps({'type':'webauthn.get','challenge':opts['publicKey']['challenge'],'origin':origin}).encode()
@@ -34,7 +37,7 @@ def assertion(origin,valid=True):
s,b,h=assertion('https://evil.example');check(s==400,'wrong origin rejected')
s,b,h=assertion('https://127.0.0.1:18765',False);check(s==400,'bad signature rejected')
s,b,h=assertion('https://127.0.0.1:18765');check(s==200 and 'redirect' in json.loads(b),'valid signed assertion completes MFA')
s,b,h=req('/admin/login?bs=account');check('Sécurité de mon compte' in b,'WebAuthn authenticated session')
s,b,h=req('/admin/plugin/pluginbluditsecure');check('Security' in b,'WebAuthn authenticated session')
# A finish request cannot reuse a consumed challenge.
s,b,h=post('key-get-finish',id=b64(cred));check(s==400,'consumed challenge rejected')
print('WebAuthn integration checks complete')