490 likes | 609 Vues
This document explores the integration of dependent types within JavaScript, focusing on their potential to provide fine-grained security through precise and flexible reasoning. Despite the significant advantages, implementing these types presents challenges, particularly with unions and mutable data. The outline includes discussions on challenges faced, tools available within Dependent JavaScript (DJS), and security predicates. Key issues of refinement types, path and flow sensitivity, handling of arrays, loops, and prototypes are also examined, aiming to elevate programming safety and reliability in JavaScript.
E N D
DependentTypes for JavaScript Ravi ChughRanjitJhala University of California, San Diego David Herman Mozilla Research
DependentTypes for JavaScript A Large Subset of Goal:Precise and Flexible Reasoningfor Fine-Grained Security But hard even for simple type invariants!
Outline • Challenges • Tour of DJS • Security Predicates
Challenges: Unions and Mutation varreadLinks= function (doc, max) { if (!max) max = 10 if (doc.domain() == “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } readLinks(document,5) // read at most 5 links … readLinks(document) // … or 10 by default integer or undefined…
Challenges: Unions and Mutation varreadLinks= function (doc, max) { if (!max) max = 10 if (doc.domain() == “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } readLinks(document,5) readLinks(document) integer or undefined…
Challenges: Unions and Mutation varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain() == “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } readLinks(document,5) readLinks(document) integer or undefined… … but now definitely an integer
Challenge: Objects varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } readLinks(document,5) readLinks(document) prototype inheritance, mutability, dynamic keys
Challenge: Arrays varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } readLinks(document,5) readLinks(document) • “length”, “holes”, • non-integer keys, prototypes
DependentJavaScript[OOPSLA ’12] • System D[POPL ’12] “Usability” refinement types F≤ ∨, ∧ + nested refinements DJS occurrence types … Coq dependent types syntactic types JS Expressivity
Outline • Challenges • Tour of DJS • Security Predicates
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Refinement Types {x|p} Bool Num Int Top {b|tag(b)=“boolean”} {n|tag(n)=“number”} {i|tag(i)=“number”integer(i)} {x|true} “value x such that formula p is true”
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Refinement Types {x|p} {x|p} 3 :: 3 :: 3 :: 3 :: {i|i>0} Int Num {i|i=3} “value x such that formula p is true” “value x such that formula p is true”
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Subtyping is Implication* {i|i=3}<:{i|i>0}<:Int <:Num i=3 i>0 tag(i)=“number”integer(i) tag(i)=“number”
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Nested Refinements • McCarthy’s decidable theory of arrays {d|Bool(sel(d,“f”))∧ sel(d,k)::IntInt} • uninterpreted System D “has-type” predicatenests typing relation inside formulas
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Nested Refinements SyntacticSubtyping + Subtyping is Implication* UninterpretedValidty Implication* =
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Path and Flow Sensitivity varreadLinks= function (doc, max) { if (!max) { max =10 } else{ } … /*: readLinks :: (Ref(~doc), Int?)Top */ max0:Int? max1:{v|v=10} (max0≠undefined) max2:{v|v=max0} max12:Int T?{x|T(x)x=undefined}
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Path and Flow Sensitivity varreadLinks= function (doc, max) { if (!max) { max =10 } else{ } … /*: readLinks :: (Ref(~doc), Int?)Top */ max0:Int? max12:Int T?{x|T(x)x=undefined}
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Path and Flow Sensitivity x1:{v|v=upd(x0,k,7)} x0:Empty varx = { } x[k]=7 Strong updates to singleton objects Weak updates to collections of objects
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Track types, “packedness,” and lengthof arrays where possible {a|a::Arr(A) {a|packed(a) {a|len(a)=10} • … • … • … • A? • -1 • 0 • A? • A? • 1 • 2 • A? • A? • len(a) • A? A?{x|A(x)x=undefined} X{x|x=undefined} • … • … • … • X • A • A • A • A • X
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes • x:T1/H1T2/H2 • (Quick Detour) • Function types includelocal heap pre- and post-conditionsà la separation logic input type output heap input heap output type
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes extern getIdx :: All A. (a:Ref, i:Int) / (a0:Arr(A)) {a1| (a1 :: A ∨ a1 = undefined)∧ 1(packed(a0) ite (0 <= i < len(a0)) 11 (a1 :: A)) 11(a1 = undefined)} / same input type / heap ite p q1 q2 (pq1) ∧ (pq2) (a1:{v|v=a0}) output type / heap
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes extern setIdx :: All A. (a:Ref, i:Int, y:A) / (a0:Arr(A)) Top / (a1:{a1:: Arr(A) ∧ 1(packed(a0) ∧0 <= i < len(a0) packed(a1) ∧len(a1) = len(a0)) ∧ 1 (packed(a0) ∧i = len(a0) packed(a1) ∧len(a1) = len(a0) + 1)})
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes extern __ArrayProto :: {v|sel(v,“pop”)::… ∧ sel(v,“push”)::… ∧ …}
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } maxinv:Int iinv:{v|v>=0} eltsinv:{a|a=elts0} Elt.protoinv:{d|…} Heap invariants before and after each iteration Type checker infers heap for common cases
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes DJS handles prototypes…
13 benchmarksmainly from SunSpider and JSGP • 300 unannotated LOC • 35% annotation overhead • 11 benchmarks run in <1s • 2 benchmarks run in 2-6s DJSProgram DesugarerBased on Guha et al.[ECOOP ’10] Z3 SMTSolver Type Checker Desugared Program
Outline • Challenges • Tour of DJS • Security Predicates
Browser Extension Security • Can we track fine-grained policies directly in JS? “Degree of JavaScript” ? ADsafetyPolitz et al.[SEC ’11] DJS JS IBEX • Guha et al.[SP ’11] ML Granularityof Invariants fine coarse
/*: readLinks :: (Ref(~doc), Int?)Top */ varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } • Allow extension to read this attribute?
/*: assume forall (e d) (eltTagName e “a” ∧ eltInDoc e d ∧ docDomain d “newyorker.com”) canReadAttr e “href”*/ • IBEX-style security policy /*: readLinks :: (Ref(~doc), Int?)Top */ varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } • Type check against IBEX-style DOM API
extern Elt.prototype.getAttr :: (this:Ref(~elt), k:Str) Str
extern Elt.prototype.getAttr :: (this:Ref(~elt),{k|Str(k)∧canReadAttr this k}) {s|Str(s)∧attrOfElt this k s}
extern Elt.prototype.getAttr :: (this:Ref(~elt),{k|Str(k)∧canReadAttr this k) {s|Str(s)∧attrOfElt this k s} extern Doc.prototype.domain :: (this:Ref(~doc)) Str
extern Elt.prototype.getAttr :: (this:Ref(~elt),{k|Str(k)∧canReadAttr this k) {s|Str(s)∧attrOfElt this k s} extern Doc.prototype.domain :: (this:Ref(~doc)) {s|Str(s)∧docDomain this s}
extern Elt.prototype.getAttr :: (this:Ref(~elt),{k|Str(k)∧canReadAttr this k) {s|Str(s)∧attrOfElt this k s} extern Doc.prototype.domain :: (this:Ref(~doc)) {s|Str(s)∧docDomain this s} extern Doc.prototype.getEltsByTagName :: …
/*: assume forall (e d) (eltTagName e “a” ∧ eltInDoc e d ∧ docDomain d “newyorker.com”) canReadAttr e “href”*/ • IBEX-style security policy /*: readLinks :: (Ref(~doc), Int?)Top */ varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } ✓ • Type check against IBEX-style DOM API
Current Status 9 of 17 IBEX examples ported to DJS Total running time ~3s Invariants translate directly (so far)
Conclusion DJS able to tracksimple type invariants, and security predicatesseem within reach
Thanks! D ravichugh.com/djs :: • github.com/ravichugh/djs
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes vargrandpa= …, parent = Object.create(grandpa), child = Object.create(parent), b= kin child, • Key Membership via Prototype Chain Unrolling b :: {v|v=trueiff (has(child,k) (has(parent,k) (has(grandpa,k) (HeapHas(H,great,k))} child parent great H (Rest of Heap) grandpa
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes vargrandpa= …, parent = Object.create(grandpa), child = Object.create(parent), b= kin child, x= child[k] • Key Lookup viaPrototype Chain Unrolling x :: {v|ifhas(child,k) then v=sel(child,k) {v|elifhas(parent,k) then v=sel(parent,k) {v|elifhas(grandpa,k) then v=sel(grandpa,k) {v|elifHeapHas(H,great,k)) then v=HeapSel(H,great,k)) {v|elsev=undefined}
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Key Idea Reduce prototype semantics to decidable theory of arrays via flow-sensitivity and unrolling
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Encode tuplesas arrays vartup= [5, “guten abend!”] {a|a::Arr(Top) {a|packed(a)len(a)=2 {a|Int(a[0]) {a|Str(a[1])}
varreadLinks= function (doc, max) { if (!max) max = 10 if (doc.domain() == “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) var i = 0 var loop = function loop () { if (i < elts.length && i <= max) { elts[i].getAttr(“href”) i++ loop() } else { undefined } } loop() } } Desugared Loop max:-Int i :-{v|v>=0} elts:-{a|a=elts0} Elt.proto:-{d|…}
{p}{v|p} /*: x:NumOrBool {iteNum(x) Num(v) Bool(v)} */ function negate(x) { x=(typeofx==“number”)? 0 – x : !x returnx } /*: x:Any{vifffalsy(x)} */ function negate(x) { x=(typeofx==“number”)? 0 – x : !x returnx }
Function Types and Objects • x:T1/H1T2/H2 input type output heap input heap output type ObjHas(d,k,H,d’)has(d,k)HeapHas(H,d’,k) /*: x:Ref / [x |-> d:Dict |> ^x] {viffObjHas(d,”f”,curHeap,^x)} / sameHeap */ function hasF(x) { return“f”in x }
Function Types and Objects • x:T1/H1T2/H2 input type output heap input heap output type ObjSel(d,k,H,d’) itehas(d,k) sel(d,k) HeapSel(H,d’,k) /*: x:Ref / [x |-> d:Dict |> ^x] {v=ObjSel(d,”f”,curHeap,^x)} / sameHeap */ function readF(x) { returnx.f }