GPUburnout commited on
Commit
fa075a3
·
verified ·
1 Parent(s): 76d8321

Security: per-fold temp file, input length cap, pinned 3Dmol + SRI

Browse files
Files changed (1) hide show
  1. app.py +25 -10
app.py CHANGED
@@ -20,6 +20,7 @@ import io
20
  import itertools
21
  import json
22
  import os
 
23
  import time
24
  import gradio as gr
25
  from ImmuneBuilder import ABodyBuilder2
@@ -490,7 +491,11 @@ CONTROLLER_JS = """
490
 
491
  if (!window.$3Dmol) {
492
  const s = document.createElement("script");
493
- s.src = "https://3Dmol.org/build/3Dmol-min.js";
 
 
 
 
494
  s.onload = () => window.myabsInit();
495
  document.head.appendChild(s);
496
  } else { window.myabsInit(); }
@@ -499,23 +504,33 @@ CONTROLLER_JS = """
499
 
500
 
501
  # ---------------------------------------------------------------------- fold
 
 
 
502
  def fold(heavy: str, light: str):
503
  """Fold VH+VL, scan liabilities, and hand the structure to the client viewer."""
504
  heavy, light = clean(heavy), clean(light)
505
  reset_btn = gr.update(value="▶ Spin")
 
 
506
  if not heavy or not light:
507
- return ("Enter both a heavy and a light chain.", "", None, "",
508
- [], [], {"H": [], "L": []}, reset_btn)
 
 
509
  try:
510
  t0 = time.time()
511
  antibody = PREDICTOR.predict({"H": heavy, "L": light})
512
  dt = time.time() - t0
513
- antibody.save("myabs_fold.pdb")
514
- except Exception as e: # OpenMM refinement / numbering can occasionally fail
515
- return (f"Fold failed: {e}", "", None, "",
516
- [], [], {"H": [], "L": []}, reset_btn)
517
-
518
- pdb = open("myabs_fold.pdb").read()
 
 
 
519
  flags, highlights, h3_len, ok = developability(heavy, light, pdb)
520
  status = (f"Folded in {dt:.1f} s · VH {len(heavy)} aa / VL {len(light)} aa · "
521
  f"CDRs highlighted (H: yellow/orange/red, L: cyan/blue).")
@@ -525,7 +540,7 @@ def fold(heavy: str, light: str):
525
  l_tokens, l_idx = build_track("L", number_chain(light) or [], highlights.get("L", []))
526
  idxmap = {"H": h_idx, "L": l_idx}
527
  payload = build_payload(pdb, highlights) # -> client-side viewer via .change bridge
528
- return (status, flags_md, "myabs_fold.pdb", payload,
529
  h_tokens, l_tokens, idxmap, reset_btn)
530
 
531
 
 
20
  import itertools
21
  import json
22
  import os
23
+ import tempfile
24
  import time
25
  import gradio as gr
26
  from ImmuneBuilder import ABodyBuilder2
 
491
 
492
  if (!window.$3Dmol) {
493
  const s = document.createElement("script");
494
+ // Pinned, immutable version + Subresource Integrity so a compromised CDN
495
+ // cannot inject arbitrary JS into users' browsers.
496
+ s.src = "https://cdn.jsdelivr.net/npm/3dmol@2.5.5/build/3Dmol-min.js";
497
+ s.integrity = "sha384-OsczYbldvrHgslr9fFp/i4GiLSeuw9l+QIlv99ITw8soOwXcoGeflFMLg+CU/X1d";
498
+ s.crossOrigin = "anonymous";
499
  s.onload = () => window.myabsInit();
500
  document.head.appendChild(s);
501
  } else { window.myabsInit(); }
 
504
 
505
 
506
  # ---------------------------------------------------------------------- fold
507
+ MAX_CHAIN_LEN = 250 # antibody variable domains are ~110-130 aa; a generous DoS cap
508
+
509
+
510
  def fold(heavy: str, light: str):
511
  """Fold VH+VL, scan liabilities, and hand the structure to the client viewer."""
512
  heavy, light = clean(heavy), clean(light)
513
  reset_btn = gr.update(value="▶ Spin")
514
+ # trailing 7 outputs (everything after `status`) for the early-return paths
515
+ tail = ("", None, "", [], [], {"H": [], "L": []}, reset_btn)
516
  if not heavy or not light:
517
+ return ("Enter both a heavy and a light chain.",) + tail
518
+ if len(heavy) > MAX_CHAIN_LEN or len(light) > MAX_CHAIN_LEN:
519
+ return (f"Sequence too long (VH {len(heavy)}, VL {len(light)} aa; max "
520
+ f"{MAX_CHAIN_LEN} per chain). Paste one antibody variable domain per box.",) + tail
521
  try:
522
  t0 = time.time()
523
  antibody = PREDICTOR.predict({"H": heavy, "L": light})
524
  dt = time.time() - t0
525
+ # Per-fold unique dir so concurrent public users never share the output
526
+ # file (the download basename stays a clean "myabs_fold.pdb").
527
+ out_path = os.path.join(tempfile.mkdtemp(prefix="myabs_"), "myabs_fold.pdb")
528
+ antibody.save(out_path)
529
+ except Exception: # OpenMM refinement / numbering can occasionally fail
530
+ return ("Fold failed. Check that both inputs are valid antibody "
531
+ "variable-domain sequences.",) + tail
532
+
533
+ pdb = open(out_path).read()
534
  flags, highlights, h3_len, ok = developability(heavy, light, pdb)
535
  status = (f"Folded in {dt:.1f} s · VH {len(heavy)} aa / VL {len(light)} aa · "
536
  f"CDRs highlighted (H: yellow/orange/red, L: cyan/blue).")
 
540
  l_tokens, l_idx = build_track("L", number_chain(light) or [], highlights.get("L", []))
541
  idxmap = {"H": h_idx, "L": l_idx}
542
  payload = build_payload(pdb, highlights) # -> client-side viewer via .change bridge
543
+ return (status, flags_md, out_path, payload,
544
  h_tokens, l_tokens, idxmap, reset_btn)
545
 
546