From 387df40c1474b2f4d0cfb8c8dcd8b074f7f79ab5 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 06:00:35 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[security?= =?UTF-8?q?=20improvement]=20Move=20staff=20verification=20to=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove hardcoded password from js/main.js - Implement /api/verify-staff endpoint in backend/main.py - Use hmac.compare_digest to prevent timing attacks - Add StaffLogin model to backend/models.py - Update .env.example with STAFF_PASSWORD - Add vite.config.js with proxy for local development - Add backend/tests/test_staff.py for verification Co-authored-by: LVT-ENG <214667862+LVT-ENG@users.noreply.github.com> --- .env.example | 3 ++ .../__pycache__/jules_engine.cpython-312.pyc | Bin 2876 -> 0 bytes backend/__pycache__/main.cpython-312.pyc | Bin 4161 -> 0 bytes backend/__pycache__/models.cpython-312.pyc | Bin 1449 -> 0 bytes .../test_jules.cpython-312-pytest-9.0.2.pyc | Bin 3028 -> 0 bytes backend/main.py | 11 +++++- backend/models.py | 3 ++ .../test_main.cpython-312-pytest-9.0.2.pyc | Bin 4712 -> 0 bytes backend/tests/test_staff.py | 36 ++++++++++++++++++ js/main.js | 28 ++++++++++---- vite.config.js | 12 ++++++ 11 files changed, 85 insertions(+), 8 deletions(-) delete mode 100644 backend/__pycache__/jules_engine.cpython-312.pyc delete mode 100644 backend/__pycache__/main.cpython-312.pyc delete mode 100644 backend/__pycache__/models.cpython-312.pyc delete mode 100644 backend/__pycache__/test_jules.cpython-312-pytest-9.0.2.pyc delete mode 100644 backend/tests/__pycache__/test_main.cpython-312-pytest-9.0.2.pyc create mode 100644 backend/tests/test_staff.py create mode 100644 vite.config.js diff --git a/.env.example b/.env.example index 178641c..34df0e4 100644 --- a/.env.example +++ b/.env.example @@ -7,5 +7,8 @@ LVT_SECRET_KEY=your_secure_secret_here # Allowed CORS origins (comma-separated list, e.g., https://yourdomain.com,http://localhost:3000) LVT_ALLOWED_ORIGINS=* +# Staff password for access to the private bunker area +STAFF_PASSWORD=your_secure_staff_password_here + # Google Gemini API Key for Jules AI advice GEMINI_API_KEY=your_gemini_api_key_here diff --git a/backend/__pycache__/jules_engine.cpython-312.pyc b/backend/__pycache__/jules_engine.cpython-312.pyc deleted file mode 100644 index e245657bb89d36f11d66b8c75097767cbc63b9ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2876 zcma)8O>7&-6`m!zb-}}XeG8BQ&0#|bvH2gc4(z9*q&I7$Sxquu^gvyXINSpYFGAu+i75zFrugI z!YgNeHCrHNte?` z-+i>)S68zAKS;0}S;g%!ZDQmNTTp8awsDi!@;cGW)J@IJEY9DU&wu#oeEyS}JB?(4 z=&RH+CUcSVbEmUwkp&Tc#UvI3MO@O@SPZ`vivHo zH~LDn!Ynp%CHISoqDh?c4=wOaYE;Nwhc&31ple{B7xgN2lO@6>Sl%KP>Z%4OAWTrO z6HI5Ku1Dm#f*}he49{T!CMXc4sr&&7qKRM*H$yN-nqipLKgRDxL|}x_inMuUG>=N* zp4wDbPJSiOTteAsV=;kY%v*wTi%@prXUbzBJdnB zDQDEd3#3TuR0!$=eu#CG$y6{0k^@dsM^G6!Ee!d9fZzlh+Yw%0v`qVHhEUV}Ig z2o*6%RX}7ti=|sP=da(wH*YV@EcqT?$>AF_pUvFFvbBIK~}3!4pjEvET@)U9)|#$ zro9fiF4XaiRWg}V_F$K)#DaGpCmVLvifvHSg{HJ9CxYgouz(qa8$AC_j${jBTCrtk0hu_WZc5q{tcur}fQ}a3zLVmDZPaD6dF=w7<293CqqiMN zYd3EPj0)7ssZj%pklboQ#aepZqYF-}6t+f}Sth+(L^f4{Sw8prh;GCH@^B;GjS{Lzia;fi_PTI_`t)tuW#+gFM@D0`BFoJBU^LZpY7afpLypg zynnu3+G(^0r=JeLu|2l)-lO66@JBz!BALWS5+IYK2U`CVZRD{wa-f}gqGcXynXR*X zZ%nndtH*+&?YG<7#PNP)yV2IB4&!Pv+Ci!s<+7)<{cgy1+<}s9mrR-q$Odjd!r2O(aZWQ7H|1-EG9an{$6zD1ryj5f+AZa%p)F1#Z zN^;{VsTYPj!{Zn(gZCHY`Q^t~dk;tucZa?zzbChnWzfFIc-im#T@PT%2Lw%y1d#s(?JAb*YU1_N=l*GfU`^r!!G^G4= jXP|>%d34=d{t@<;y*t18x0D`+Q7Gf79Zn4?evtnQD5ve? diff --git a/backend/__pycache__/main.cpython-312.pyc b/backend/__pycache__/main.cpython-312.pyc deleted file mode 100644 index 326c4676eb24f1cf191f394946f00e85b58a9928..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4161 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 tA+e>%`NatV$ra~wx249&2dGr*Mf+MQif znOjRL@eiU_xmOice?+gnB;wH3NKvKs#4Qv~J@uW%CRJ6b>KvYV=6#=cXXcri{W+P8 zBWT~f_Y*(T5&B&X`Yq8T=RX2@jwqs1PwG|fdeU5Pd%Wck z+v?Q^U2om!d=7@s5kn1)qDJTmVqvCJ?LI^U77CaFvG7$)SFCH4BGP=~GEujPBcmN) zm{VzZtv%*Q;|roDkF$N|c-ZTFK2xHagl3 z1`i+k3B^*`XX|3PFZ_2?HX;2#Oi}g(rmzSwRDc59&dd4ITYq8ECmDd*mrrpje{ zaiO}lP^sa1eP#~VtCh8b+#<1?ggeE$&6`sz+$%m}#Bom-K)h)Tc-n{>cjx1Z_Ui#F8=n_4+l4PIQ_6(c6VECzTGW97I0lGZ|idRraiRFF1U#cbkw+~QtWOsM2L3a%+dy1KkospHw{+qk~DvAp@o z!-G_n?Q^#Xp<;cHJ5Kj>36wkEexusi18J<-9XK+x?h%I)K~JY3-H8HBL&<-y`0m|% z_vYtIAyI@9p?t5E2Bg{UQaDX+3H2gn1YQbvc7(dW!cY)V5Zvoa=j#vGA6HhM;!166 zxwg5!@l@)zdTEh;Vt1G;wfBezcB8PUGvjLKliSm&id&IKljmDHa-^V wDX#Ywc|TVS6k~nGSz&Y}^b)1dX3${fW#+~&nVYXNH%~GjoFu2dj0bo0FC3e8bN~PV diff --git a/backend/__pycache__/test_jules.cpython-312-pytest-9.0.2.pyc b/backend/__pycache__/test_jules.cpython-312-pytest-9.0.2.pyc deleted file mode 100644 index a7209d82add22b51559619728437e61a573a2a6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3028 zcmZ`)-E$My6~C+9l~%GWTQ)Y59q2lkfC`o!;y~jNiol9T@dvGC(jbOzm)&c5&1$9D zUD=i@r)FS^+u;GGNnnPgC4He3I>VpP5_sy1T*TPDz;veJp?&iNI+L`0=(&>CG8wuf z?K$_{bM8GKzjOA_&CNapPiW^|EiNGRC0#g=r_Mb434_oTq#~8kQHrTjSB6zw49ajR zo~iA)Q|{`HC*`S9AteBH>E4Vl*>BMQqq(i2;c&R+ z7Q{fGZ_~Q>t(jn|jX^GieYa|g2)9Y?b+#7amedxAh}AnXovF^M9j9;2gi;~k`SU0h z#$BrW%x;9t5S$~-?su#k6y#y&e8&?86_^4O@%)pjO+*;t9hcLZGM-h=z#?d3C7a1$ zLzU993DgFmaNNRXQjra%aWq1-UgzNd?AI=YE_g-}168x@aU-Z+<3^ix3>|M4p;jd% zfo1I}A=~Znvw)0mKeJWbyeO46EbJC%M`cw`&n>PdTVyu*B$SxhR!6r%uOZd@ z8QbvJi&USAbnl|8;knv$+wW+UT+)U=_EzVd5~DWX4&188bPlTcQ;7Q$_6qxRo=1!9 zCOt^`@bg{Gp*{?YH=|$$7i~OcjLiTr@y%O(O zz8HJ84y}vsl6!WCquZd4j|@5i*Fhl=|qj zeqPoroA~e?bf#n%a<~|n!j`QWX^}SQkXU4+(Wpr=I0c>SH?f^J4QWo+^OyvW#Ky*B zi3us0+_zs!jwQxH#aHihxFz2MX19>m5pHne$f%~2K=AB2Ct8q+PEp9Q!p$RnG*90*BttUxxz5$ zqK^YmR?E&z+(Z$UxS|7uo6njmao2pX=wYI`^zW{IIhX4{U5mBfhD=Jcc^zBPT!DBG zrm=w+a^|68kC`_lRg=?37ILjb59(P()~!R)4HY_kE&9dz`XA`)B~%U!-i%!DUA8aH zUHtH7^UX=C^k8fm>3xX0E%?TYFPuT;&j$Lt*I(DKxUhoVnvH@ z`%Wg0A3YjRCQkuUscWF6>)Clww`Z`JGqpKcDO6EcM`+H>+F2#5M~i;n$q!D6@yW?! zlS5*pjqspt*d`4g5{?&a;;%+4VI!>>nDCjbignUT<2Gd*OG?(T!8@}VEKHuh{8ihG ztQn+f4x8F^L6YYtZYE`;tLAS(ZC36*(UMYrmSF^Y2vNAA>jojB9P_` ze0U_YjxtcAHLw86=1zK~8+P)#O}rbqAq+P6P*>=8*P@>avGyD`w^NJ2i?uTMfiOr_ zhX75r>OAJWW#7RH_s(-S>e&9P(WUXV@QJ1I3fH-IVEFvlD%ZKfb*^%|R=8aiZV;4$ z;kDMCYk}Qsfwr~w@N+L8bT1{I2N3UDp1AtfYIpxicYnES;0{~%4OX}}*WF$G_Gbd} zZh73@d#AV3ePEfZ2%)E2cmFP2{{CcTYjQQ1Ty{Sdn#+NG72%D?Lh~izYUg!fB^)V- zzFQ9LsR(;PSPty12z|gTPnSD(t^{7I2)owZs4Y|u?Oq8+R)hU3!Ttxq*O%R7%jmNA zX;a_<#T~pVO@oj9!E)P?hyJ5aDf5Ge{vWPyLv2&cH_t`n@AwL_{C0Y8^QX%7o@>)r zKPXp^esy&`n43foqMZU0&Kfa-s(W01phvEFzy^QYZAhyCboGc)Yt?gnE0 zu=wH~1~$Gp)CBxJFXiv~7|Q#-aUXlHV^5rC?;{@M_jw*9_kAqzM35v3B}t0BLWI+K zO@~2d5&z*_LE2b_rCfo6A}U)j3n`$YCZ0Dn8x!w_h|Yh)2~-L(X8ZIc-&Uz{zP%$C5enJUZulguIVX;0f9R2w}Jj!dc-$%h{Got}C9) zo+~YvTduoqcs}>sX!*S5j_^Qy>p|;K1-<<*(Cz(esOMVK5??_bU!wLW=+!5v`zx-G p*}nc`266u7;$PVC8rQNka$)rB=;gqJR`DU%v(6%}%X|-f_#cWj=v4p! diff --git a/backend/main.py b/backend/main.py index fb88c85..7df20f4 100644 --- a/backend/main.py +++ b/backend/main.py @@ -7,7 +7,7 @@ from fastapi.responses import JSONResponse from fastapi.middleware.cors import CORSMiddleware from dotenv import load_dotenv -from models import UserScan, SHOPIFY_INVENTORY +from models import UserScan, StaffLogin, SHOPIFY_INVENTORY from jules_engine import get_jules_advice # Load .env file @@ -28,6 +28,7 @@ # 🛡️ Configuración Maestra (abvetos.com) - Secrets moved to environment variables SECRET_KEY = os.getenv("LVT_SECRET_KEY", "DEVELOPMENT_SECRET_DO_NOT_USE_IN_PROD") +STAFF_PASSWORD = os.getenv("STAFF_PASSWORD", "DEVELOPMENT_STAFF_PASS") PATENT = "PCT/EP2025/067317" def verify_auth(user_id: str, token: str) -> bool: @@ -49,6 +50,14 @@ def calculate_fit(user_waist: float, item_id: str): is_perfect = 0.95 <= fit_index <= 1.05 return is_perfect, round(fit_index, 3), item +@app.post("/api/verify-staff") +async def verify_staff(login: StaffLogin): + # 🛡️ Use hmac.compare_digest to prevent timing attacks + if hmac.compare_digest(login.password, STAFF_PASSWORD): + return {"status": "SUCCESS", "message": "Access granted"} + else: + raise HTTPException(status_code=401, detail="Access denied") + @app.post("/api/recommend") async def recommend_garment(scan: UserScan, garment_id: str = "BALMAIN_SS26_SLIM"): # 1. Seguridad y Handshake diff --git a/backend/models.py b/backend/models.py index 652f4cd..8a33bba 100644 --- a/backend/models.py +++ b/backend/models.py @@ -7,6 +7,9 @@ class UserScan(BaseModel): waist: float event_type: str # e.g., 'Gala', 'Business', 'Cocktail' +class StaffLogin(BaseModel): + password: str + class Garment(BaseModel): id: str name: str diff --git a/backend/tests/__pycache__/test_main.cpython-312-pytest-9.0.2.pyc b/backend/tests/__pycache__/test_main.cpython-312-pytest-9.0.2.pyc deleted file mode 100644 index 8ee35419d16682196cede9269ec7f338b8e49468..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4712 zcmb^!&2QV*{ZXPmX?2dg~{^M#=GXqh%85+v95W24_Fe+VJ;PlB)~=mCez06ah#VNOLWoI3|9!BT)j6@DeeQBHUz44i-( zsYF+zE-k3WD)E&#hXP2B%JIh>wYMT*{ytjis&Vh6=2#0Y5M{(>S-w0Tq z1%x)BBu5_{w*!9`m~JCz2R9(5qfdj!U5${<+o8?#2sOfX*!+tXV^SM#(HTZ(oUp@d z98MCQwZLQEqo%9`liNr#s69XSz-?sq`s}CM5)86~tO15!vYYYSb6c{?;grwnak4zK z`UbfWlf&tU!8%l|5y2VUZATQ&#iCX(i@;~8kI~w*9c7l-{5uY3?{N(t-Z0cWxG~t4 zW)Q1WBZhk#aXW6uwoyZ{1@n(SZ^w0|*A}2FeMF<(6`#&=HEAt%-2Z>;UW{e?#_qKw zp?m#!;9AwgACGd?!j&rG8o&<>~^0zgADWVh77uy z96sk|Fo4hFyglG$FnB@+gG`S@J`cm~E-}dF@h{mqt{``poWbwlckMHthoMvNk|E}k z&1{!==aK+(2;Ky|NqE!nrr^!Mn{9O0vibMwJMkPmk=8{+yzNjE*Nr2t5oDF z&cUl|O4Z7hGy|xXTFb3VRjkTpZcS3uEkib4BWs3MarJL)5#HQQ&@3xeId{G+TjB;G ziW1&dN^*YORj3cFN~KLOKVFfP>iD*7C~GxQ+OpP5t;^6WTNdU+ayvahaDxXHm;Z$F z0f*aiILjomx!g}95Vn57vv|RRj7)bFUE_MqiC$5q$|{zw)@KM;Y-K2xB?B_GL>jN? zWi@_9)k>0TUIlXi45!^-wGq}9uC0p09mcC19<6Qt`hS_+ zu~x`cZjzgS5B)9K-yFE~G&$XjP8(f7s=w9Iwxa{d2*Wc+}jPJ8n zDY*FI+$RfjHy6dFrNXqh^vTTyC>_6HYSoudz)zn0DNrotx%%YjdA(N1oADhF$Q5%0 zx|lb|O-r)2OtGY4Io`+>DdSS!bfU1Rb**a3`JfX7SuzyJaH*b@Oo-ZYZOniu5N8&h zq-oVurCMeyoO1$76})#7R}*~Z;(|~@B2_v$=b~= zsY>O&8;X^?B}-LU3%UV&Msgz8m2KEw8uJ3CnV0FjnGYDFV1?M9u181|vozRQkneG$ z@`W`qMKl~9BARf=se06v0ZT4E-O{?;c2j_|dRmu>RG~n%3SiB}r(TjhP*3?vRv$Tx-)hNA^)?j)JSs-~%$o*#CEidNl}Yr15W){P92jp$NM)g)|?!`~4| zcr^V9KQiS_eJ0E>sTli-uvjs zG)nhbLitIcR$;Cn)=P&#~|6&^IRA?5JsB(*rQ1pn!?DV$pc~RIZuAyc?62J z#shxrvssEgZG+ETVM_ihG1roB0U-TC$bMP;y!gfYd)HqGLrwl{QyBW%Y6@qc^W^uP zN1)hK7o%`%1f8#~7N3DFHUOaZI)FmA17P+QBETc7`R4mM zezM&BboF4NM6vyW=0d4CSE4e_vhL|x_W-DKw*s#b+Tv~pJhIF8@O2>n+coVUJweyE zpAJ_u4b@NlffCOZ{lt^te|c#@zGcz?i8^XzsC`UU(jwM$bcsrh+c)MedIq}CzP zqtbq%{{^NIxfgpr;Y8UIe2;5-~R { window.location.href = "/staff-dashboard"; }, 1500); - } else { - this.showNotification('ACCESO DENEGADO', 'error'); - input.value = ""; + const password = input.value; + + try { + // 🛡️ Verify credentials via backend to avoid hardcoded secrets in JS + const response = await fetch('/api/verify-staff', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ password }) + }); + + if (response.ok) { + this.showNotification('ACCESO CONCEDIDO', 'success'); + setTimeout(() => { window.location.href = "/staff-dashboard"; }, 1500); + } else { + this.showNotification('ACCESO DENEGADO', 'error'); + input.value = ""; + } + } catch (error) { + console.error('Staff verification failed:', error); + this.showNotification('ERROR DE CONEXIÓN AL BÚNKER', 'error'); } } } diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..7af6018 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite'; + +export default defineConfig({ + server: { + proxy: { + '/api': { + target: 'http://localhost:8000', + changeOrigin: true, + }, + }, + }, +});