├── .pr-preview.json ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── Makefile ├── README.md ├── index.html ├── index.src.html ├── published ├── 2015-10-FPWD.html └── default.css └── w3c.json /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.src.html", 3 | "type": "bikeshed", 4 | "params": { 5 | "force": 1 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.7" 4 | before_install: 5 | - sudo apt-get update -qq 6 | - sudo apt-get install -qq python-dev python-pip libxslt1-dev libxml2-dev 7 | - sudo pip install pygments 8 | install: 9 | - git clone --depth=1 --branch=master https://github.com/tabatkins/bikeshed.git ./bikeshed 10 | - sudo pip install --editable ./bikeshed 11 | - bikeshed update 12 | script: 13 | - bikeshed spec ./index.src.html 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Application Security Working Group 2 | 3 | Contributions to this repository are intended to become part of Recommendation-track documents 4 | governed by the [W3C Patent Policy](https://www.w3.org/Consortium/Patent-Policy/) and 5 | [Document License](https://www.w3.org/Consortium/Legal/copyright-documents). To contribute, you must 6 | either participate in the relevant W3C Working Group or make a non-member patent licensing 7 | commitment. 8 | 9 | If you are not the sole contributor to a contribution (pull request), please identify all 10 | contributors in the pull request's body or in subsequent comments. 11 | 12 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 13 | 14 | ``` 15 | +@github_username 16 | ``` 17 | 18 | If you added a contributor by mistake, you can remove them in a comment with: 19 | 20 | ``` 21 | -@github_username 22 | ``` 23 | 24 | If you are making a pull request on behalf of someone else but you had no part in designing the 25 | feature, you can remove yourself with the above syntax. 26 | 27 | # Tests 28 | 29 | See [CONTRIBUTING.md](https://github.com/w3c/webappsec/blob/master/CONTRIBUTING.md). 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All documents in this Repository are licensed by contributors under the [W3C Document 2 | License](http://www.w3.org/Consortium/Legal/copyright-documents). 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: index.html 2 | 3 | force: 4 | bikeshed -f spec -l ./index.src.html 5 | 6 | index.html: index.src.html 7 | bikeshed -f spec -l ./index.src.html 8 | 9 | publish: 10 | git push origin master master:gh-pages 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Confinement with Origin Web Labels 2 | ======================= 3 | 4 | [![Join the chat at https://gitter.im/w3c/webappsec-cowl](https://badges.gitter.im/w3c/webappsec-cowl.svg)](https://gitter.im/w3c/webappsec-cowl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | [![Build Status](https://travis-ci.org/w3c/webappsec-cowl.svg)](https://travis-ci.org/w3c/webappsec-cowl) 6 | 7 | * COWL is being specified in `index.src.html` 8 | * [Published Editor's Draft](https://w3c.github.io/webappsec-cowl/) 9 | * [W3C First Public Working Draft](http://www.w3.org/TR/2015/WD-COWL-20151015/) 10 | 11 | -------------------------------------------------------------------------------- /index.src.html: -------------------------------------------------------------------------------- 1 |

Confinement with Origin Web Labels

2 |
   3 | Status: ED
   4 | Group: WebAppSec
   5 | TR: https://www.w3.org/TR/COWL/
   6 | ED: https://w3c.github.io/webappsec-cowl/
   7 | Shortname: COWL
   8 | Level: 1
   9 | Editor: Deian Stefan, Intrinsic and UC San Diego, deian@intrinsic.com
  10 | Editor: Abdulrahman Alkhelaifi, UC San Diego, aalkhela@eng.ucsd.edu
  11 | Abstract:
  12 |   This specification defines an API for specifying privacy and
  13 |   integrity policies on data, in the form of origin labels, and a
  14 |   mechanism for confining iframes according to such policies.  This
  15 |   allows Web application authors and server operators to shared data
  16 |   with untrusted—buggy but not malicious—code (e.g., in a mashup
  17 |   scenario) yet impose restrictions on how the code running in an iframe can
  18 |   share the data further.
  19 | Indent: 2
  20 | Version History: https://github.com/w3c/webappsec-cowl/commits/master/index.src.html
  21 | Boilerplate: omit conformance, omit feedback-header
  22 | !Participate: File an issue (open issues)
  23 | Inline Github Issues: true
  24 | Markup Shorthands: css off, markdown on
  25 | 
26 | 31 |
  32 | spec: CORS; urlPrefix: https://www.w3.org/TR/cors/
  33 |   type: dfn
  34 |     text: CORS
  35 |     text: Access-Control-Allow-Origin; url: access-control-allow-origin-response-header
  36 | spec: IndexedDB; urlPrefix: https://www.w3.org/TR/IndexedDB-2/
  37 |   type: dfn
  38 |     text: IndexedDB
  39 | spec: WEBMESSAGING; urlPrefix: https://www.w3.org/TR/webmessaging/
  40 |   type: method
  41 |     text: postMessage(); url: dom-window-postmessage
  42 |   type: dfn
  43 |     text: posting messages; url: posting-messages
  44 |     text: owner; url: concept-port-owner
  45 |     text: Message Port; url: messageport
  46 |     text: MessagePort postMessage(); url: dom-messageport-postmessage
  47 |     text: cross-document messaging; url: web-messaging
  48 | spec: WORKERS; urlPrefix: https://www.w3.org/TR/workers/
  49 |   type: dfn
  50 |     text: Worker
  51 |     text: Web Worker; url: infrastructure
  52 | spec: CSP2; urlPrefix: https://www.w3.org/TR/CSP2/
  53 |   type: dfn
  54 |     text: content-security-policy
  55 |   type: dfn
  56 |     text: host-source; url: host_source
  57 | spec: HTML5; urlPrefix: https://www.w3.org/TR/html5/
  58 |   type: element-attr
  59 |     urlPrefix: embedded-content-0.html
  60 |       text: sandbox; for: iframe; url: attr-iframe-sandbox
  61 |       text: contentDocument; for: iframe; url: dom-iframe-contentdocument
  62 |   type: dfn
  63 |     urlPrefix: webappapis.html
  64 |       text: environment settings object; url: settings-object
  65 |       text: incumbent settings object; url: incumbent-settings-object
  66 |     urlPrefix: embedded-content-0.html
  67 |       text: iframe; url: the-iframe-element
  68 |     urlPrefix: browsers.html
  69 |       text: browsing context; url: windows
  70 |       text: top-level browsing context; url: top-level-browsing-context
  71 |       text: active sandboxing flag set; url: active-sandboxing-flag-set
  72 |       text: sandboxing flag set; url: sandboxing-flag-set
  73 |       text: forced sandboxing flag set; url: forced-sandboxing-flag-set
  74 |       text: sandboxed plugins browsing context flag;url: sandboxed-plugins-browsing-context-flag
  75 |       text: sandboxed document.domain browsing context flag;url: sandboxed-document.domain-browsing-context-flag
  76 |       text: sandboxed origin browsing context flag;url: sandboxed-origin-browsing-context-flag
  77 |       text: sandboxed navigation browsing context flag;url: sandboxed-navigation-browsing-context-flag
  78 |       text: sandboxed auxiliary navigation browsing context flag;url: sandboxed-auxiliary-navigation-browsing-context-flag
  79 |       text: sandboxed top-level navigation browsing context flag;url: sandboxed-top-level-navigation-browsing-context-flag
  80 |       text: navigating; url: navigate
  81 |     urlPrefix: infrastructure.html
  82 |       text: structured clone; url: structured-clone
  83 |       text: structurally cloned; url: structured-clone
  84 |       text: structurally clonable; url: structured-clone
  85 |       text: internal structured cloning algorithm; url: internal-structured-cloning-algorithm
  86 |       text: strictly split a string
  87 |       text: skip whitespace
  88 |       text: collect a sequence of characters
  89 |       text: strip leading and trailing whitespace
  90 |       text: strip and collapse whitespace
  91 |       text: ASCII case-insensitive
  92 |       text: space characters; url: space-character
  93 |     urlPrefix: dom.html
  94 |       text: script from reading from or writing to the document.cookie IDL attribute; url: sandboxCookies
  95 |   type: interface
  96 |     urlPrefix: browsers.html
  97 |       text: Window; url: window
  98 |     urlPrefix: dom.html
  99 |       text: Document; url: the-document-object
 100 | spec: feature-policy; urlPrefix: https://wicg.github.io/feature-policy/
 101 |   type: dfn
 102 |     text: container policy; url: container-policies
 103 | spec: RFC6454; urlPrefix: https://tools.ietf.org/html/rfc6454
 104 |   type: dfn
 105 |     text: origin; url: section-3.2
 106 |     text: globally unique identifier; url: section-2.3
 107 | spec: RFC4122; urlPrefix: https://tools.ietf.org/html/rfc4122
 108 |   type: dfn
 109 |     text: UUID; url: section-3
 110 | spec: URL; urlPrefix: https://www.w3.org/TR/url/
 111 |   type: interface
 112 |     text: URL; url: concept-url
 113 |   type: dfn
 114 |     text: scheme; url: concept-url-scheme
 115 |   type: attribute
 116 |     text: host; for: URL; url: concept-url-host
 117 |     text: path; for: URL; url: concept-url-path
 118 |     text: port; for: URL; url: concept-url-port
 119 |     text: scheme; for: URL; url: concept-url-scheme
 120 | spec: RFC7159; urlPrefix: https://tools.ietf.org/html/rfc4627
 121 |   type: dfn
 122 |     text: JSON; url: section-2
 123 |     text: JSON object; url: section-2.2
 124 |     text: JSON array; url: section-2.3
 125 |     text: JSON stringification; url: section-2
 126 | spec: html-comms; urlPrefix: https://html.spec.whatwg.org/multipage/comms.html
 127 |   type: dfn
 128 |     text: Server-sent events; url: server-sent-events
 129 | spec: whatwg-messaging; urlPrefix: https://html.spec.whatwg.org/multipage/web-messaging.html
 130 |   type: dfn
 131 |     text: channel messaging; url: channel-messaging
 132 |     text: broadcast channels; url: broadcasting-to-other-browsing-contexts
 133 | spec: WebRTC; urlPrefix: https://www.w3.org/TR/webrtc/
 134 |   type: dfn
 135 |     text: WebRTC
 136 | spec: XHR; urlPrefix: https://xhr.spec.whatwg.org/
 137 |   type: dfn
 138 |     text: XMLHttpRequest
 139 |     text: final MIME type; url: final-mime-type
 140 |     text: author request headers; url: author-request-headers
 141 |   type: attribute
 142 |     text: responseType; url: dom-xmlhttprequest-responsetype
 143 |     text: response; url: dom-xmlhttprequest-responsetype
 144 |   type: interface
 145 |     text: XMLHttpRequest; url: xmlhttprequest
 146 |   type: enum
 147 |     text: XMLHttpRequestResponseType; url: xmlhttprequestresponsetype
 148 |   type: method
 149 |     text: send(); url: the-send()-method
 150 |     text: open(); url: the-open()-method
 151 | spec: FETCH; urlPrefix: https://fetch.spec.whatwg.org/
 152 |   type: dfn
 153 |     text: fetching
 154 |     text: main fetch; url: main-fetch
 155 |     text: request; url: concept-request
 156 |     text: response; url: concept-response
 157 |     text: body; url: concept-response-body
 158 |     text: client; url: concept-request-client
 159 |     text: type; url: concept-request-type
 160 |     text: destination; url: concept-request-destination
 161 |     text: header; url: concept-header
 162 |     text: name; url: concept-header-name
 163 |     text: value; url: concept-header-value
 164 |     text: header list; url: concept-header-list
 165 |     text: network error; url: concept-network-error
 166 |     text: extracting a MIME type; url: concept-header-extract-mime-type
 167 |     text: navigation request; url: navigation-request
 168 | spec: ENCODING; urlPrefix: https://encoding.spec.whatwg.org/
 169 |   type: dfn
 170 |     text: utf-8 decode; url: utf-8-decode
 171 | spec: WEBSTORAGE; urlPrefix: https://www.w3.org/TR/webstorage/
 172 |   type: attribute
 173 |     text: localStorage; url: the-localstorage-attribute
 174 | spec: RFC5234; urlPrefix: https://tools.ietf.org/html/rfc5234
 175 |   type: dfn
 176 |     text: ALPHA; url: appendix-B.1
 177 |     text: DIGIT; url: appendix-B.1
 178 |     text: WSP; url: appendix-B.1
 179 |     text: VCHAR; url: appendix-B.1
 180 | spec: DOM-Parsing; urlPrefix: https://w3c.github.io/DOM-Parsing/
 181 |   type: dfn
 182 |     text: serialized; url: dfn-concept-fragment-serializing-algorithm
 183 | spec: WEBIDL2; urlPrefix: https://heycam.github.io/webidl/
 184 |   type: interface
 185 |     text: object; url: idl-object
 186 |     text: DOMString; url: idl-DOMString
 187 |   type: dfn
 188 |     text: exposed; url: dfn-exposed
 189 |     text: SecureContext; url: SecureContext
 190 | spec: REFERRER-POLICY; urlPrefix: https://w3c.github.io/webappsec-referrer-policy/
 191 |   type: dfn
 192 |     text: determine request's referrer algorithm; url: determine-requests-referrer
 193 |     text: referrer policy; url: referrer-policy
 194 | spec: WEBSOCKETS; urlPrefix: https://www.w3.org/TR/websockets/
 195 |   type: method
 196 |     text: WebSocket(); url: dom-websocket
 197 |   type: dfn
 198 |     text: make disappear; url: make-disappear
 199 | 
200 | 201 | 202 |
 203 | {
 204 |   "DCLabels": {
 205 |     "authors": ["Deian Stefan", "Alejandro Russo", "David Mazieres", "John C. Mitchell"],
 206 |     "title": "Disjunction Category Labels",
 207 |     "href": "http://www.scs.stanford.edu/~deian/pubs/stefan:2011:dclabels.pdf",
 208 |     "publisher": "Springer Berlin Heidelberg"
 209 |   },
 210 |   "COWL-OSDI": {
 211 |     "authors": ["Deian Stefan", "Edward Z. Yang", "Petr Marchencko", "Alejandro Russo", "David Herman", "Brad Karp", "David Mazieres", "John C. Mitchell"],
 212 |     "title": "Protecting Users by Confining JavaScript with COWL",
 213 |     "href":"https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-stefan.pdf",
 214 |     "publisher": "USENIX"
 215 |   },
 216 |   "URL": {
 217 |     "authors": [ "Anne van Kesteren", "Sam Ruby" ],
 218 |     "title": "URL",
 219 |     "href": "https://www.w3.org/TR/url",
 220 |     "status": "WD",
 221 |     "publisher": "W3C"
 222 |   },
 223 |   "WEBIDL2": {
 224 |     "authors": [ "Cameron McCormack", "Boris Zbarsky" ],
 225 |     "title": "Web IDL (Second Edition)",
 226 |     "href": "https://heycam.github.io/webidl/",
 227 |     "status": "ED",
 228 |     "publisher": "W3C"
 229 |   }
 230 | }
 231 | 
232 | 233 | 242 |
243 |

Introduction

244 | 245 | This section is not normative. 246 | 247 | Modern Web applications are conglomerations of JavaScript written by 248 | multiple authors. Authors routinely incorporate third-party scripts 249 | into their applications and share user data with third-party 250 | services (e.g., as part of a mashup). Unfortunately, in 251 | the existing model, the user's data confidentiality and integrity is 252 | put at risk when one incorporates untrusted third-party code or 253 | shares data with untrusted third-party services. 254 | 255 | Mechanisms such as CORS and CSP can be used to mitigate these risks 256 | by giving authors control over whom they share data with. But, once 257 | data is shared, these mechanisms do not impose any restrictions on 258 | how the code that was granted access can further disseminate the 259 | data. 260 | 261 | This document specifies an extension to the current model called 262 | Confinement with Origin Web Labels (COWL). COWL provides authors 263 | with APIs for specifying (mandatory) access control policies on 264 | data, including content, in terms of origin labels. These 265 | policies are enforced in a mandatory fashion, transitively, even 266 | once code has access to the data. For example, with COWL, the 267 | author of https://example.com can specify that a 268 | password is confidential to https://example.com (and 269 | thus should only be disclosed to https://example.com) 270 | before sharing it with a third-party password strength checking 271 | service. In turn, COWL ensures that the third-party service, which 272 | necessarily computes on the sensitive password, is confined and 273 | respects the policy on the password: COWL disallows it from 274 | disclosing the password to any origin other than 275 | https://example.com. 276 | 277 | COWL enforces such policies by confining code at the 278 | browsing context-level, according to the sensitivity (i.e., the label) 279 | of the data the code has observed. To reap the greatest benefits of 280 | COWL, authors will need to compartmentalize applications into 281 | multiple contexts (e.g., iframes). 282 | 283 | In the existing model, any page served from an origin has the 284 | ambient, implicit authority of that origin. This documents 285 | generalizes this notion of authority and gives authors explicit 286 | control over it with privileges. For example, by default, a 287 | page whose origin is https://example.com has the 288 | privilege for https://example.com. This gives the page 289 | the authority to arbitrarily disseminate data sensitive to 290 | https://example.com; to be backwards-compatible, 291 | COWL does not confine the page when reading data sensitive to 292 | https://example.com. However, COWL allows the author 293 | to run iframes with "weaker" delegated privileges (e.g., one 294 | corresponding the current user at https://example.com) 295 | or to drop the privilege altogether. 296 | 297 | COWL is intended to be used as a defense-in-depth mechanism that can restrict 298 | how untrusted—buggy but not malicious—code handles sensitive 299 | data. Given the complexities of browser implementations and the presence of 300 | covert channels, malicious code may be able to exfiltrate data. Authors 301 | should still use discretionary access control mechanisms, such as CSP and 302 | CORS, to restrict access to the data in the first place. 303 | 304 |
305 |

Goals

306 | 307 | The goal of COWL is to provide authors with a means for protecting 308 | the confidentiality and integrity of data that is shared with 309 | untrusted code, whether third-party or their own. Existing 310 | mechanisms (e.g., 311 | CORS's Access-Control-Allow-Origin header and the 312 | targetOrigin argument to 313 | postMessage()) provide a way for restricting 314 | which origins may access the shared data. But, once content 315 | has access to data it can usually disseminate it without 316 | restrictions. While CSP can be used to confine code, i.e., restrict 317 | how confidential data is disseminated, setting a correct CSP policy 318 | (as to confine code) is difficult and limited to content the author 319 | has control over. Indeed, sharing confidential data in the existing 320 | model almost always requires the sender to trust the receiver not to 321 | leak the data, accidentally or otherwise. COWL provides a 322 | defense-in-depth option for protecting data confidentiality and 323 | integrity. In particular, with COWL: 324 | 325 | 1. Authors should be able to specify confidentiality and integrity 326 | policies on data in terms of origin labels: the origins to whom the 327 | data is confidential and the origins that endorse the data. This 328 | allows authors to share sensitive data with third-party content and 329 | impose restrictions on the origins with which it can communicate 330 | once it inspects the sensitive data. Dually, it allows authors to 331 | share data via intermediate content while retaining its integrity. 332 | 333 | 2. Authors should be able to run code with least privilege 334 | by restricting the origins the code can communicate with and thus 335 | how it can disseminate sensitive data. 336 | 337 | 3. Authors should be able to privilege separate 338 | applications by compartmentalizing them into separate contexts that 339 | have delegated privileges. 340 | 341 |
342 | 343 |
344 |

Use Cases/Examples

345 |

Confining untrusted third-party services

346 | 347 | An author wishes to use a service, loaded in the form of an cowl iframe, 348 | without trusting it (or its dependencies) to not leak her 349 | sensitive data. To protect the data, the author associates a 350 | confidentiality label with the data, specifying the origins 351 | allowed to read the data. The author then shares the newly created 352 | labeled object with the untrusted code. In turn, COWL 353 | confines the untrusted code once it inspects the sensitive data, as 354 | to ensure that it can only communicate according to the 355 | author-specified policy (the label). 356 | 357 |
358 | The author of https://example.com 359 | wishes to use a third-party password strength checker provided by 360 | https://untrusted.com. To protect the 361 | confidentiality of the password, the 362 | https://example.com application can use COWL to 363 | run the checker code in a cowl iframe and associate a 364 | confidentiality policy, in the form of a label, with the password 365 | before sending it to the untrusted service: 366 | 367 |

 368 |     // Create new policy using Labels that specifies that the password is sensitive
 369 |     // to https://example.com and should only be disclosed to this origin:
 370 |     var policy = new Label(window.location.origin);
 371 | 
 372 |     // Associate the label with the password:
 373 |     var labeledPassword = new LabeledObject(password, { confidentiality: policy });
 374 | 
 375 |     // Send the labeled password to the checker iframe:
 376 |     checker.postMessage(labeledPassword, "https://untrusted.com");
 377 | 
 378 |     // Register listener to receive a response from checker, etc.
 379 |     
380 | 381 | Once the checker inspects the label-protected object, i.e., the 382 | password, COWL limits the iframe to communicating with origins that 383 | preserve the password's confidentiality (in this case, 384 | https://example.com). This policy is enforced mandatorily, 385 | even if the https://untrusted.com iframe sends the password to 386 | yet another iframe. 387 | 388 | Note, until the checker actually inspects the labeled password, it 389 | can freely communicate with any origins, e.g., with 390 | https://untrusted.com. This is important since the 391 | checker may need to fetch resources (e.g., regular expressions) to 392 | check the password strength. It's also safe—the checker has 393 | not inspected the sensitive password, and thus need not be 394 | confined. 395 |
396 | 397 | Other use cases in this category include password managers and 398 | encrypted document editors, for example, where an 399 | encryption/decryption layer and a storage layer are provided by 400 | distrusting, but not malicious, services. The academic paper on 401 | COWL describes these use cases in detail [[COWL-OSDI]]. 402 | 403 |

Sharing data with third-party mashups

404 | 405 | A server operator wishes to provide third-party mashups access to user data. 406 | In addition to using CORS response headers to restrict the origins 407 | that can access the data [[!CORS]], the operator wishes to restrict how the 408 | data is further disseminated by these origins once they have access to it, in 409 | the browser. To do so, the perator sends a response header field named 410 | Sec-COWL (described in 411 | [[#response-header]]) whose value contains the sensitivity of the data in the 412 | form of a serialized confidentiality label. In turn, COWL enforces 413 | the label restrictions on the third-party code. 414 | 415 |
416 | The server operator of https://provider.com uses a 417 | CORS response header to grant https://mashup.com 418 | access to a resource. The operator also sets a COWL header to 419 | specify that the resource is confidential to 420 | https://provider.com and should not be disseminated 421 | arbitrarily: 422 | 423 |

 424 |      Access-Control-Allow-Origin: https://mashup.com
 425 |      Sec-COWL: data-confidentiality https://provider.com
 426 |     
427 | 428 | COWL only allows a https://mashup.com context to read 429 | the sensitive response if the label restrictions of the response 430 | are respected, i.e., if the code can only communicate with 431 | https://provider.com. 432 | 433 | Note, COWL only allows the code to inspect the response if the context 434 | labels, which dictate the context's ability to communicate, are more 435 | restricting than the labels of the response. A more permissive approach, 436 | which does not require the context give up its ability to communicate 437 | arbitrarily is to use a labeled JSON response. The mashup XHR example shows how authors can 439 | accomplish this. 440 |
441 | 442 |

Content isolation via privilege separation

443 | 444 | A server operator wishes to isolate content (e.g., of different 445 | users) while serving it from a single physical origin. The 446 | operator can leverage privileges to ensure that content of 447 | one part of the site has different authority from another and, 448 | importantly, does not have the authority of the physical origin. 449 | Concretely, when serving content, the operator can set the 450 | content's context privilege to a weaker, delegated 451 | privilege. This ensures that the content are privilege 452 | separated. 453 | 454 |
455 | Suppose https://university.edu wished to isolate 456 | different parts of their site according to users. The server 457 | operator can weaken the privilege of a page when serving user 458 | content by providing a response header field named 459 | Sec-COWL 460 | (see [[#response-header]]) whose value contains 461 | a serialized delegated privilege. For example, for any 462 | content under https://university.edu/~user1, the 463 | following header is set: 464 | 465 |

 466 |      Sec-COWL: ctx-privilege 'self' OR app:user1
 467 |     
468 | 469 | Having this privilege can be understood as having the authority of 470 | user1's part of the application's origin. COWL ensures that 471 | the content of this page cannot interfere with the content of 472 | https://university.edu or that of another user e.g., 473 | user2. For example, the content cannot modify 474 | https://university.edu cookies or the DOM of another 475 | https://university.edu page. 476 | 477 | This delegated privilege also ensures that the 478 | content cannot disseminate data sensitive to another user (e.g., 479 | user2) arbitrarily—without being confined, 480 | it can only disseminate user1's data on 481 | https://university.edu. 482 | Of course, this requires the server operator to label 483 | sensitive data (e.g., when sending it to the user agent) appropriately 484 | (e.g., user2's data is labeled 485 | Label("https://university.edu").or("app:user2")). 486 | 487 | The sub-origin isolation in 488 | JavaScript example shows how this can be implemented using the 489 | COWL JavaScript APIs. 490 |
491 | 492 |
493 | Note, sub-domains should be used when possible to ensure that 494 | content is isolated using the Same-Origin Policy. But, even in 495 | such cases, COWL can provide a useful layer of defense. 496 |
497 | 498 |

Running content with least-privileges

499 | 500 | An author wishes to use a library that is tightly coupled with the page 501 | (e.g., jQuery), but not trust it to protect the user's confidentiality and 502 | integrity. With COWL, the author can do this by dropping privileges 503 | (from the context's default privilege) and then loading the untrusted 504 | library. In dropping privileges, the content (and untrusted library) loses 505 | its implicit authority over the content's origin. 506 | 507 |
508 | The author of https://example.com can drop privileges 509 | in JavaScript: 510 | 511 |

 512 |     // Drop privileges, by setting the context privilege to an empty privilege:
 513 |     COWL.privilege = new Privilege();
 514 | 
 515 |     // Load untrusted library
 516 |     
517 | 518 | Or, by setting the content's initial privilege to the empty 519 | privilege using 520 | Sec-COWL response header: 521 | 522 |

 523 |      Sec-COWL: ctx-privilege 'none'
 524 |     
525 | 526 |
527 | Note, while this ensures that the context code cannot, for instance, 528 | access the origin's cookies, the author must still associate a 529 | confidentiality label with resources (e.g., HTTP responses) to 530 | ensure that data is properly protected. 531 |
532 |
533 | 534 | In some cases it is useful for a particular context to have the 535 | privilege to disseminate certain categories of data. (The 536 | or part of labels can be used to easily 537 | categorize differently-sensitive data.) To this end, the author 538 | should run the context with a delegated privilege instead of 539 | the empty privilege. The above [[#examples-isolation]] shows 540 | one such example. 541 |
542 | 543 |
544 |

Security Considerations

545 |

Information leakage via covert channels

546 | COWL provides developers with a way of imposing restrictions on how 547 | untrusted code can disseminate sensitive data. However, authors should 548 | avoid sharing sensitive data with malicious code, since such code may be 549 | able to exploit covert channels to leak the data. Covert channels are 550 | prevalent in most browsers. COWL can only prevent information leakage from 551 | code that uses overt communication channels. 552 | 553 | Similarly, COWL provides no guarantees against attacks wherein users are 554 | manipulated into leaking sensitive data via out-of-band channels. For 555 | example, an attacker may be able to convince a user to navigate their 556 | user agent to an attacker-owned origin by entering a URL that contains 557 | sensitive information into the user agent's address bar. 558 | 559 |

Defense in depth

560 | COWL has been defined within the context of existing security mechanisms 561 | (CSP, SRI, CORS, and iframe sandbox) 562 | and SHOULD be used as an additional layer of defense. The goal of this 563 | specification is not to replace existing discretionary access control 564 | mechanisms. 565 |
566 |
567 |

Privacy Considerations

568 |

Encoding information in labels

569 | This specification introduces the 570 | Sec-COWL HTTP request header to 571 | provide server operators with information about the context that made the 572 | request (e.g., the context privilege). This header is sent in 573 | accordance to thereferrer policy [[REFERRER-POLICY]]. However, 574 | since the request header may contain serialized application-specific 575 | principals, this can have privacy implications when the principals 576 | encode private user information. It is RECOMMENDED that authors and 577 | server operators not include any private information in 578 | labels—labels SHOULD be treated as public data. 579 | 580 |

Using labels to enhance privacy

581 | COWL provides mechanisms for restricting how information flows from 582 | within the confines of the user agent. Thus, from a privacy perspective, 583 | application developers, server operators, and specification authors are 584 | encouraged to consider using these mechanisms (namely, labels) to 585 | prevent unwanted or accidental information leaks. 586 |
587 |
588 | 589 | 598 |
599 |

Key Concepts and Terminology

600 | 601 |

Labels

602 | 1. An origin label, or more succinctly a 603 | label, encodes either a confidentiality or integrity 604 | security policy as conjunctive normal form (AND's and OR's) 605 | formulae over principals (typically origins.) Labels can 606 | be associated with browsing contexts or with structurally clonable 607 | objects. 608 | 609 | When associated with a browsing context, the label restricts the 610 | origins that the context can communicate with, as detailed in 611 | [[#framework-context-labels]]. 612 | 613 |
614 | The confidentiality label 615 | Label("https://a.com").or("https://b.com"), 616 | when associated with a context, 617 | restricts the context to sending data to 618 | https://a.com or https://b.com, but 619 | no other origins. 620 | This context label reflects the fact the context may contain data 621 | that is sensitive to either https://a.com or 622 | https://b.com; it is thus only safe for it to 623 | communicate to these origins. 624 | 625 | Note, because the context can communicate data to either origin, 626 | another context associated with the more restricting 627 | label Label("https://a.com") cannot 628 | send it data. Doing so would allow for data confidential 629 | to https://a.com to be leaked to https://b.com. 630 |
631 | 632 |
633 | The integrity label 634 | Label("https://a.com").or("https://b.com"), 635 | when associated with a context, restricts the context to 636 | receiving data from (a context or server) that is at least as 637 | trustworthy as https://a.com or 638 | https://b.com. This context label ensures that 639 | the code running in the context can only be influenced by data 640 | which either https://a.com or 641 | https://b.com endorse. 642 |
643 | 644 | When associated with an object, a confidentiality label 645 | specifies the origins to whom the object is sensitive, while an 646 | integrity label specifies the origins that endorse the object. 647 | Objects that have labels associated with them are called 648 | labeled objects. [[#object-labels]] defines how 649 | labels are associated with objects. 650 | 651 |
652 | Consider an https://example.com page that 653 | receives a labeled object (e.g., via postMessage()) 654 | with the following labels: 655 | 656 | * Confidentiality: 657 | Label("https://example.com"). 658 | This label indicates that the object is sensitive to 659 | https://example.com. 660 | 661 | * Integrity: 662 | Label("https://a.com"). This 663 | label indicates that the object has been endorsed by 664 | https://a.com. If 665 | https://example.com received the message from an 666 | intermediary https://b.com context, this label 667 | reflects the fact that the object (produced by 668 | https://a.com) was not tampered. 669 |
670 | 671 | 2. A principal is a string that represents an authoritative 672 | entity. There are three kind of principals: 673 | 674 | - An origin principal is the stringified URL 675 | (scheme/host/port triple) of an origin. 676 | 677 | - A unique principal is a stringified globally unique 678 | identifier that contains the "unique:" string as a 679 | prefix to a UUID (see unique-principal-expression). 680 | 681 | - An application-specific principal is used by authors to 682 | encode application-specific security entities (e.g., users, services); 683 | they are strings prefixed by the "app:" string (see 684 | app-principal-expression). Such principals, when used in 685 | labels, typically only make sense if combined (e.g., as a 686 | disjunction) with origin principals or unique principals. 687 | 688 | 689 | 3. Mathematically, a label is a conjunctive normal form 690 | formula over principals [[DCLabels]]. 691 | 692 | A label is in normal form if reducing it according to 693 | the label normal form reduction algorithm produces the 694 | same value. 695 | 696 | Two labels are equivalent if their normal form 697 | values are mathematically equal. 698 | 699 | A label A subsumes (or is more 700 | restricting than) another label 701 | B if the result of running the label subsumption 702 | algorithm on the normal forms of A and 703 | B returns true. Labels are partially 704 | ordered according to this subsumes relation. 705 | 706 | The current confidentiality label is the 707 | confidentiality label associated with the current context. 708 | [[#framework-context-labels]] specifies how labels are associated with 709 | contexts. 710 | 711 | The current integrity label is the 712 | integrity label associated with the current context. 713 | [[#framework-context-labels]] specifies how labels are associated with 714 | contexts. 715 | 716 | When reading a labeled object, a context gets 717 | tainted, i.e., its context labels are updated by 718 | invoking context tainting algorithm, to reflect that it 719 | has read sensitive data, or data of potentially different 720 | trustworthiness, and should be confined accordingly. 721 | 722 |

Privileges

723 | 1. A privilege is an unforgeable object that corresponds 724 | to a label. Privileges are associated with contexts and 725 | reflect their authority. 726 | 727 | Privileges can be used to bypass confinement restrictions imposed by 728 | confidentiality labels. In particular, a privilege can be used to bypass 729 | the restrictions imposed by any label that is subsumed by the privilege's 730 | corresponding label—the internal privilege label. 731 | 732 |
733 | Consider a context from https://a.com whose 734 | current confidentiality label is Label("https://a.com").and("https://b.com"). 736 | This label confines the context to only communicating with 737 | entities whose labels are at least as restricting as this 738 | label. For example, it restricts the context from 739 | communicating with a context labeled Label("https://b.com"), since doing 741 | so could leak https://a.com data to 742 | https://b.com. It similarly prevents the context 743 | from communicating with https://a.com. 744 | 745 | But, suppose that the context's current privilege 746 | corresponds to Label("https://a.com") 747 | (afterall, the context originated from https://a.com). 748 | Then, the context would be able to bypass some of the 749 | restrictions imposed by the context label. Specifically, the 750 | context would be able to communicate with 751 | https://b.com; the privilege confers it the right 752 | to declassify https://a.com data to 753 | https://b.com. Indeed, when taking this privilege 754 | into consideration, the effective confidentiality label 755 | of the context is Label("https://b.com"). 756 | 757 | Note, the privilege does not allow the context to bypass any 758 | label restrictions. For example, it does not allow the context 759 | to communicate with https://a.com since doing so 760 | could leak https://b.com data. 761 | 762 |
763 | To be flexible, COWL uses the context privilege to remove 764 | certain restrictions imposed by the context label. 765 | To avoid accidentally leaking sensitive context data, 766 | authors should use LabeledObjects. 767 |
768 | 769 |
770 | 771 | Privileges can also be used to bypass integrity restrictions 772 | imposed by integrity labels. In particular, a privilege can be 773 | used to endorse an otherwise untrustworthy labeled 774 | browsing context (or labeled object) as to allow it to 775 | communicate with more trustworthy end-points (another context or 776 | server). 777 | 778 |
779 | Consider an https://a.com context whose 780 | current integrity label is 781 | Label("https://a.com").or("https://b.com"). 782 | This label confines the context to only communicating with 783 | entities that are at most as trustworthy as this 784 | label. For example, it restricts the context from 785 | communicating with a context whose current integrity 786 | label is 787 | Label("https://a.com"), since doing 788 | so would potentially corrupt https://a.com data 789 | (e.g., by allowing https://b.com to influence the 790 | computation). 791 | 792 | But, if the context's current privilege corresponds 793 | to Label("https://a.com"), the 794 | context would be able to bypass some of these integrity restrictions. 795 | Specifically, the context would be able to communicate with the 796 | more-trustworthy context (labeled 797 | Label("https://a.com")) since the 798 | privilege confers it the right to endorse (or vouch for) its 799 | context on behalf of https://a.com. Indeed, when 800 | taking privileges into account, the effective integrity 801 | label of the context is Label("https://a.com"). 803 | 804 | Note, the privilege cannot be used to bypass any integrity 805 | restrictions. For example, it does not allow the context to 806 | communicate with a context whose integrity label is Label("https://b.com"). 808 |
809 | 810 | Note, browsing contexts have a current privilege 811 | that, by default, corresponds to the origin of the context, as 812 | described in [[#framework-context-labels]]. But, authors should set the current 813 | privilege to a delegated privilege to follow 814 | the principle of least privilege. 815 | 816 | 2. The current privilege is the privilege associated 817 | with the current context. [[#framework-context-labels]] specifies how 818 | privileges are associated with contexts. 819 | 820 | 3. The effective confidentiality label is the label 821 | returned by the label downgrade algorithm when invoked 822 | with the current confidentiality label and current 823 | privilege. 824 | 825 | 4. The effective integrity label is the label 826 | returned by the label upgrade algorithm when invoked 827 | with the current integrity label and current 828 | privilege. 829 | 830 | 5. Code can take ownership of a privilege 831 | priv by setting the current privilege to the 832 | privilege produced via the combination of the current 833 | privilege and priv. In doing so, it is said 834 | that the context owns the privilege. 835 |
836 | 837 | 846 |
847 |

Framework

848 | 849 | This sub-section is not normative. 850 | 851 | In a nut-shell, the COWL framework provides: 852 |
853 |
Policy specification via labels
854 |
855 | COWL provides a Label interface for specifying 856 | confidentiality and integrity policies in terms of 857 | principals (typically, origins). Labels can be 858 | associated with data and content using the JavaScript LabeledObject and COWL interfaces or the 860 | Sec-COWL HTTP header field. 861 |
862 |
Explicit authority via privileges
863 |
864 | The COWL framework provides a JavaScript 865 | Privilege interface for operating on and minting 866 | new privileges. The COWL JavaScript 867 | interface and Sec-COWL HTTP response header can be 868 | used to explicitly control the authority of a context by setting 869 | the context privilege. 870 |
871 |
Confinement enforcement mechanism
872 |
873 | COWL extends browsing contexts, in particular iframes 874 | with labels and privileges, which are used when enforcing confinement, 875 | i.e., when restricting a context's network and cross-context messaging 876 | communication. This document defines the necessary changes and 877 | extensions to existing browser constructs and algorithms to enforce 878 | confinement. 879 |
880 |
881 | 882 |
883 |

Labels

884 | 885 | Each label is an immutable object represented by a 886 | Label object, the interface of which is defined in 887 | this section. 888 | 889 | A Label MUST have an internal label set, which is 890 | a non-empty set of disjunction sets. 891 | 892 | A disjunction set is a set of principals. 893 | 894 | A label is said to be an empty label if its label 895 | set contains a single, empty disjunction set. 896 | 897 |
 898 |       [Constructor, Constructor(DOMString principal), Exposed=(Window, Worker)]
 899 |       interface Label {
 900 |         boolean equals(Label other);
 901 |         boolean subsumes(Label other, optional Privilege priv);
 902 | 
 903 |         Label and((Label or DOMString) other);
 904 |         Label _or((Label or DOMString) other);
 905 | 
 906 |         stringifier;
 907 |       };
 908 |     
909 | 910 |

Constructors

911 |
912 |
Label()
913 |
914 | When invoking the Label() constructor, the user agent 915 | MUST return a new empty label. 916 |
917 |
Label(DOMString principal)
918 |
919 | When invoking the Label(principal) constructor, the user agent 920 | MUST use an algorithm equivalent to the following: 921 | 922 | 1. If the principal 923 | argument is not a principal, the constructor MUST throw a 924 | TypeError exception [[!ECMA-262]] and 925 | terminate this algorithm. 926 | 927 | 2. Else, it MUST return a new Label that contains a 928 | label set of a single disjunction set, which 929 | itself MUST contain the principal corresponding to the 930 | principal argument. 931 |
932 |
933 | 934 |

Methods

935 |
936 |
equals(Label other)
937 |
938 | The user agent MUST return true if the Label on 939 | which the method has been called is equivalent to the 940 | other 941 | parameter; otherwise it MUST return false. 942 |
943 |
subsumes(Label other, optional Privilege priv)
944 |
945 | The user agent MUST use an algorithm equivalent to the 946 | following: 947 | 948 | 1. Let lab be the Label on which the method 949 | has been called. 950 | 951 | 2. If the priv argument is 952 | provided, let lab be 953 | lab.and(priv.asLabel()). 954 | 955 | 3. Return true if lab subsumes the 956 | other 957 | parameter and false otherwise. 958 |
959 |
and((Label or DOMString) other)
960 |
961 | The user agent MUST use an algorithm equivalent to the following: 962 | 963 | 1. Let O be the 964 | other argument. 965 | 966 | 1. If the type of other is 967 | DOMString, run the following sub-steps: 968 | 969 | 1. Set O to the result of invoking the 970 | Label(other) constructor with other as an argument, if the 972 | constructor did not raise an exception. 973 | 974 | 2. Else, re-throw the exception and terminate this algorithm. 975 | 976 | 2. Return a new normal form Label 977 | that is equivalent to a label whose 978 | label set contains the disjunction sets of 979 | O and the Label on which 980 | the method was invoked. 981 |
982 |
or((Label or DOMString) other)
983 |
984 | The user agent MUST use an algorithm equivalent to the following: 985 | 986 | 1. Let O be the 987 | other argument. 988 | 989 | 1. If the type of other is 990 | DOMString, run the following sub-steps: 991 | 992 | 1. Set O to the result of invoking the 993 | Label(other) constructor with other as an argument, if the 995 | constructor did not raise an exception. 996 | 997 | 2. Else, re-throw the exception and terminate this algorithm. 998 | 999 | 2. Return a new Label, in normal form, 1000 | which is equivalent to adding each element of each 1001 | disjunction set of O's label set to 1002 | each disjunction set of the label set of the 1003 | Label on which the method was called. 1004 |
1005 |
1006 |

Serializing labels

1007 | 1008 | The stringification behavior MUST return the 1009 | serialization of the label, which adheres to the label-expression 1010 | grammar, using an algorithm equivalent to the following: 1011 | 1012 | 1. Let label be the label being serialized. 1013 | 1014 | 2. If label is the empty label, return `"'none'"` and 1015 | terminate this algorithm. 1016 | 1017 | 3. Let result be the empty string. 1018 | 1019 | 3. For each element dset in the label's label set: 1020 | 1021 | 1. If result is not the empty string, append the characters `" AND "` to it. 1022 | 1023 | 2. If the number of elements in dset is greater than 1: 1024 | 1025 | 1. Append the character "(" (U+0028) to result. 1026 | 1027 | 2. Append the character " " (U+0020) to result. 1028 | 1029 | 3. Let o be the empty string. 1030 | 1031 | 4. For each element prin in the disjunction set dset: 1032 | 1033 | 1. If o is not the empty string, append the characters `" OR "` to it. 1034 | 1035 | 2. Append prin to o. 1036 | 1037 | 5. Append o to result. 1038 | 1039 | 6. If the number of elements in dset is greater than 1: 1040 | 1041 | 1. Append the character " " (U+0020) to result. 1042 | 1043 | 2. Append the character ")" (U+0029) to result. 1044 | 1045 | 1046 |

Examples

1047 | 1048 |
1049 | Example labels. Intuition for each label's semantics is given in 1050 | the context of it being used as a confidentiality label (C) and 1051 | integrity label (I). 1052 |

1053 |       // C: Public data.
1054 |       // I: Non-endorsed/untrustworthy data.
1055 |       var empty = new Label();
1056 | 
1057 |       // C: Data confidential to a.com.
1058 |       // I: Data endorsed/trusted by a.com.
1059 |       var a = new Label("https://a.com");
1060 | 
1061 |       // C: Data confidential to b.com.
1062 |       // I: Data endorsed/trusted by b.com.
1063 |       var a = new Label("https://b.com");
1064 | 
1065 |       // C: Data confidential to both a.com and b.com
1066 |       // I: Data endorsed/trusted by a.com and b.com.
1067 |       var aANDb = new Label("https://a.com").and("https://b.com");
1068 | 
1069 |       // C: Data confidential to either a.com or b.com.
1070 |       // I: Data endorsed/trusted by either a.com or b.com.
1071 |       var aORb = new Label("https://a.com").or("https://b.com");
1072 |       
1073 | Examples of label comparisons with intuition for the semantics. 1074 |

1075 |       // C: Data confidential to a.com (b.com) data is more sensitive than public data.
1076 |       // I: Data endorsed by a.com (b.com) is more trustworthy than non-endorsed/untrustworthy data.
1077 |       a.subsumes(empty) === true;
1078 |       b.subsumes(empty) === true;
1079 | 
1080 |       // C: Data that is confidential to a.com and b.com is more
1081 |       //    confidential than data that is only sensitive to a.com (b.com).
1082 |       // I: Data that is endorsed/trusted by both a.com and b.com is
1083 |       //    more trustworthy than data endorsed only by a.com (b.com).
1084 |       aANDb.subsumes(a) === true;
1085 |       aANDb.subsumes(b) === true;
1086 | 
1087 |       // C: Data that that is confidential to a.com (b.com) is not comparable to
1088 |       //    data that is confidential to b.com (a.com).
1089 |       // I: Data that that is endorsed by a.com (b.com) is not comparable to
1090 |       //    data that is endorsed by b.com (a.com).
1091 |       a.subsumes(b) === false;
1092 |       b.subsumes(a) === false;
1093 | 
1094 |       // C: Data that is confidential to a.com (b.com) is more confidential than data that is
1095 |       //    confidential to either a.com or b.com. Alternative intuition: data that can be read by
1096 |       //    a.com or b.com can be read by an entity that can read a.com (b.com) data alone.
1097 |       // I: Data that is endorsed by a.com (b.com) is more trustworthy than data that is endorsed
1098 |       //    by either a.com or b.com. Alternative intuition: an entity that trusts data endorsed
1099 |       //    by either a.com or b.com necessarily trusts data endorsed by a.com (b.com) alone.
1100 |       a.subsumes(aOrb) === true;
1101 |       b.subsumes(aOrb) === true;
1102 |       
1103 |
1104 | 1105 |
1106 | Using the labels defined in the above example, this example shows how 1107 | labels are serialized. We only define an additional label: 1108 |

1109 |       var aORbANDc = aORb.and(new Label("https://c.com");
1110 |       
1111 | 1112 | Converting to string: 1113 |

1114 |       empty.toString()    === "'none'";
1115 |       a.toString()        === "https://a.com";
1116 |       aANDb.toString()    === "(https://a.com) AND (https://b.com)";
1117 |       aORb.toString()     === "https://a.com OR https://b.com";
1118 |       aORbANDc.toString() === "(https://a.com OR https://b.com) AND (https://c.com)";
1119 |       
1120 |
1121 |
1122 | 1123 |
1124 |

Privileges

1125 | 1126 | Each privilege is an immutable object represented by a Privilege object, the interface of which is 1128 | defined in this section. 1129 | 1130 | A Privilege MUST have an internal privilege 1131 | label. 1132 | 1133 | The combination of privileges A and 1134 | B is a privilege produced by invoking the 1135 | combine() method on A (respectively, 1136 | B) with B (respectively, A) as an 1137 | argument. 1138 | 1139 | A privilege is said to be an empty privilege if its 1140 | internal privilege label is the empty label. A 1141 | context is said to be unprivileged if its context 1142 | privilege is the empty privilege. By setting the 1143 | context privilege to the empty privilege, a context 1144 | is said to be dropping privileges. 1145 | 1146 | A privilege P1 is said to be a delegated 1147 | privilege of P2 if P2's internal 1148 | privilege label subsumes P1's internal 1149 | privilege label. 1150 | 1151 |
1152 |       [Constructor, Exposed=(Window, Worker), SecureContext]
1153 |       interface Privilege {
1154 |         static Privilege FreshPrivilege(); // Named constructor
1155 |         Label asLabel();
1156 | 
1157 |         Privilege combine(Privilege other);
1158 |         [Throws] Privilege delegate(Label label);
1159 |       };
1160 |     
1161 | 1162 |

Constructors

1163 |
1164 |
Privilege()
1165 |
1166 | When invoking the Privilege() constructor, the user 1167 | agent MUST return a new Privilege that has its 1168 | internal privilege label set to the empty-label. 1169 |
1170 |
FreshPrivilege()
1171 |
1172 | 1173 | When invoking the FreshPrivilege() constructor, the user 1174 | agent MUST use an algorithm equivalent to the following: 1175 | 1176 | 1. Let unique principal be a freshly generated unique principal. 1177 | 1178 | 2. Let unique label be the label produced by invoking the 1179 | Label(principal) constructor with unique principal. 1180 | 1181 | 3. Return a new Privilege that has an 1182 | internal privilege label set to unique label. 1183 | 1184 |
1185 |
1186 | 1187 |

Methods

1188 |
1189 |
asLabel()
1190 |
1191 | The user agent MUST return the internal privilege label of the 1192 | Privilege on which the method has been called. 1193 |
1194 |
combine(Privilege other)
1195 |
1196 | 1197 | The user agent MUST return a new Privilege whose internal 1198 | privilege label is equivalent to a label 1199 | created according to an algorithm equivalent to the following: 1200 | 1201 | 1. Let internalLabel be the 1202 | internal privilege label of the 1203 | Privilege on which the method has been called. 1204 | 1205 | 2. Let otherLabel be the internal privilege label of 1206 | the other argument. 1207 | 1208 | 3. Return internalLabel.and(otherLabel). 1209 | 1210 |
1211 |
delegate(Label label)
1212 |
1213 | 1214 | The user agent MUST return a new Privilege whose internal 1215 | privilege label is equivalent to a label 1216 | created according to an algorithm equivalent to the following: 1217 | 1218 | 1. Let internalLabel be the 1219 | internal privilege label of the 1220 | Privilege on which the method has been called. 1221 | 1222 | 2. If the internalLabel does not subsume the 1223 | label 1224 | argument, throw a SecurityError 1225 | exception and terminate this algorithm. 1226 | 1227 | 3. Else, return a new Privilege that has an 1228 | internal privilege label set to 1229 | label. 1230 | 1231 |
1232 |
1233 | 1234 |
1235 | Note, the Privilege constructors and the 1236 | combine() and delegate() methods only provide ways 1237 | for creating privileges. Context code must still take 1238 | ownership of or set the current privilege to the 1239 | privilege for it to be used (to bypass label restrictions). 1240 |
1241 | 1242 |

Examples

1243 | 1244 |
1245 | To be backwards-compatible with the Same-Origin Policy, COWL 1246 | grants each browsing context a default privilege 1247 | that corresponds to their origin. For example, a page on 1248 | https://example.com has a privilege whose internal 1249 | privilege label is 1250 | Label("https://example.com"). 1251 | 1252 | As a result, reading data that is sensitive to 1253 | Label("https://example.com") does 1254 | not confine the context. For example, reading a 1255 | labeled object whose confidentiality label is 1256 | Label("https://example.com") does 1257 | not restrict the context from communicating—and thus 1258 | accidentally leaking that object's contents—to another origin. 1259 | To prevent accidental leaks, the author should drop privileges by 1260 | setting the current privilege to an empty 1261 | privilege: 1262 |

1263 |         // Save privilege in case we need it later:
1264 |         var __savedPriv = COWL.privilege;
1265 | 
1266 |         // Drop privilege (set the context privilege to the empty privilege):
1267 |         COWL.privilege = new Privilege();
1268 |       
1269 | 1270 | After this point, if the context reads data with a 1271 | Label("https://example.com") 1272 | confidentiality label, COWL will restrict it to communicating 1273 | with https://example.com. 1274 |
1275 | 1276 |
1277 | Consider an extension to the password strength checker example of 1278 | [[#examples-checker]] that uses FreshPrivilege()s to 1279 | ensure that the untrusted checker cannot communicate with any 1280 | entity other than the parent context. 1281 |

1282 |         // Create new fresh privilege:
1283 |         var priv = new FreshPrivilege();
1284 | 
1285 |         // Take ownership of the fresh privilege:
1286 |         COWL.privilege = COWL.privilege.combine(priv);
1287 | 
1288 |         // Associate the unique label with the password:
1289 |         var labeledPassword = new LabeledObject(password, {confidentiality: priv.asLabel()});
1290 | 
1291 |         // Send the labeled password to the checker iframe:
1292 |         checker.postMessage(labeledPassword, "https://untrusted.com");
1293 |       
1294 | Once the https://untrusted.com cowl iframe reads the 1295 | password it will be tainted by the unique, internal 1296 | privilege label of priv; the unique origin 1297 | ensures that it cannot send the password to, for example, public 1298 | parts of https://example.com. Indeed, only the 1299 | owner of priv can disseminate the labeled password 1300 | (result) arbitrarily. 1301 |
1302 | 1303 |
1304 | Consider an implementation of the content isolation example of 1305 | [[#examples-isolation]] using the COWL JavaScript API. 1306 | In this example, the author https://university.edu isolates 1307 | different parts of their site according to 1308 | users. For user1 it can do this as follows: 1309 | 1310 |

1311 |         // Create a label corresponding to the university origin:
1312 |         var uni = new Label(window.location.origin);
1313 |         // Create a new label that corresponds to user1's data on university.edu:
1314 |         var user1 = uni.or("app:user1"); // Here the app:user1 is an application-specific princial
1315 | 
1316 |         // Originally, COWL.privilege.asLabel().equals(uni).
1317 |         // Drop the current context privilege to a delegated privilege:
1318 |         COWL.privilege = COWL.privilege.delegate(user1);
1319 |       
1320 | 1321 | At this point, the context can only arbitrarily disseminate data 1322 | that is labeled 1323 | Label("https://university.edu").or("app:user1"); 1324 | it cannot disseminate data that is 1325 | sensitive to the university (e.g., which is labeled 1326 | Label("https://university.edu")) 1327 | or to another user (e.g., user2's data is 1328 | labeled 1329 | Label("https://university.edu").or("app:user2")). 1330 |
1331 |
1332 | 1333 |
1334 |

Labeled Contexts

1335 | 1336 | COWL extends browsing contexts with a COWL state, which 1337 | is used to restrict the context's communication channels. The COWL state 1338 | consists of: 1339 | 1340 | * The confinement mode status, which indicates whether 1341 | or not COWL confinement is enabled and thus labels should 1342 | be enforced in the current context. 1343 | 1344 | A context is considered a confined context if its 1345 | confinement mode status is set, and a unconfined 1346 | context otherwise. 1347 | 1348 | Note: confinement mode is currently only be enabled for contexts 1349 | instantiated in cowl iframes. In future versions we may 1350 | generalize the enforcement to other browsing contexts and 1351 | Workers. 1352 | 1353 | * The context labels, which consist of: 1354 | 1355 | * context confidentiality label reflects the 1356 | sensitivity of the data that the context has read. 1357 | 1358 | * context integrity label reflects the integrity of 1359 | the data that the context has read. 1360 | 1361 | * The context privilege, which encodes the context's 1362 | ability to bypass the restrictions of certain labels. 1363 | 1364 | Each context's COWL state MUST be initially set to the 1365 | default COWL state, where: 1366 | 1367 | * The confinement mode is disabled. 1368 | 1369 | * The context confidentiality label is set to the 1370 | default confidentiality label: empty label. 1371 | 1372 | * The context integrity label is set to the 1373 | default integrity label: empty label. 1374 | 1375 | * The context privilege is set to the 1376 | default privilege: a 1377 | privilege whose internal privilege label is 1378 | equivalent to Label(origin), where 1379 | origin is the string representation of the context or 1380 | Worker's origin. 1381 | 1382 |
1383 | The default COWL state is backwards-compatible with the 1384 | existing Web model. Specifically: 1385 | 1386 | * Unless confinement mode is enabled, a context is not 1387 | subject to confinement. 1388 | 1389 | * Unless the current confidentiality label 1390 | and the current integrity label are 1391 | non-empty labels, the context's communication 1392 | is unrestricted; code is only 1393 | subject to the restrictions imposed by other mechanisms such as 1394 | the Same-Origin Policy and CSP. 1395 | 1396 | * Unless the current privilege is dropped or set to a 1397 | delegated privilege, code can disseminate data sensitive 1398 | to the browsing context's origin, even when confinement 1399 | mode is enabled. Such data is implicitly declassified 1400 | using the context privilege. Authors should send 1401 | LabeledObjects to explicitly communicate the 1402 | sensitivity (and integrity) of the data they are sharing. 1403 |
1404 | 1405 | COWL restricts the code running in a confined context from 1406 | communicating with other contexts or remote servers in a way that preserves 1407 | confidentiality and integrity (see [[#enforcement]]). 1408 | 1409 | In general, COWL does not restrict code running in unconfined 1410 | contexts from communicating with other unconfined contexts or 1411 | remote servers. To preserve this invariant COWL does, however, prevent 1412 | such code from reading overly sensitive data (i.e., data that would 1413 | taint the context) or communicating with certain confined 1414 | contexts and COWL-enabled servers. 1415 | 1416 | Note: the COWL state of an unconfined context does not change 1417 | from its original default COWL state. 1418 | 1419 | The COWL state is exposed to confined contexts via the 1420 | COWL interface defined below. This interface MUST not be 1421 | exposed to unconfined contexts. 1422 | 1423 |
1424 |       [SecureContext, COWLContext]
1425 |       interface COWL {
1426 |         [SetterThrows] static attribute Label confidentiality;
1427 |         [SetterThrows] static attribute Label integrity;
1428 |         [SetterThrows] static attribute Privilege privilege;
1429 |       };
1430 |     
1431 | 1432 |

Attributes

1433 |
1434 |
confidentiality
1435 |
1436 | 1437 | * On getting, the user agent MUST return the current 1438 | confidentiality label. 1439 | 1440 | * On setting, the user agent MUST use an algorithm equivalent 1441 | to the following: 1442 | 1443 | 1. Let conf be the set confidentiality label. 1444 | 1445 | 2. Let canWrite be the result of invoking 1446 | the write check algorithm with conf 1447 | and the current integrity label. 1448 | 1449 | 3. If canWrite is false, throw a 1450 | SecurityError exception and terminate this 1451 | algorithm. 1452 | 1453 | 4. Else, set the current confidentiality label to conf. 1454 | 1455 |
1456 |
integrity
1457 |
1458 | 1459 | * On getting, the user agent MUST return the current 1460 | integrity label. 1461 | 1462 | * On setting, the user agent MUST use an algorithm equivalent 1463 | to the following: 1464 | 1465 | 1. Let int be the set integrity label. 1466 | 1467 | 2. Let canWrite be the result of invoking the 1468 | write check algorithm with the current 1469 | confidentiality label and int. 1470 | 1471 | 3. If canWrite is false, 1472 | throw a SecurityError 1473 | exception and terminate this algorithm. 1474 | 1475 | 4. Else, set the current integrity label to int. 1476 | 1477 |
1478 |
privilege
1479 |
1480 | 1481 | * On getting, the user agent MUST return the current 1482 | privilege. 1483 | 1484 | * On setting, the user agent MUST use an algorithm equivalent to the following: 1485 | 1486 | 1. Let priv be the set privilege. 1487 | 1488 | 2. Set the current privilege to priv. 1489 | 1490 |
1491 |
1492 | 1493 |

Examples

1494 | Below are several examples showing how to use the COWL API. The [[#privilege-examples]] illustrate 1496 | the use of context privileges. 1497 | 1498 |
1499 | An author can set the context integrity label to ensure 1500 | that the context can only receive messages from 1501 | (another context or server of) the same origin: 1502 |

1503 |       COWL.integrity = new Label(window.location.origin);
1504 |       
1505 |
1506 | 1507 |
1508 | The author of https://mashup.com can set the context 1509 | confidentiality label to receive data sensitive from 1510 | https://provider.com: 1511 |

1512 |       COWL.confidentiality = new Label('https://provider.com');
1513 |       
1514 | At this point, the context can only communicate with 1515 | https://provider.com. The data provider can 1516 | ensure that only appropriately labeled contexts 1517 | can inspect an HTTP response by setting response labels using 1518 | the Sec-COWL response 1519 | header. 1520 |
1521 |
1522 | 1523 |
1524 |

Labeled Objects

1525 | 1526 | A LabeledObject interface represents an immutable 1527 | object that is protected by a confidentiality and integrity 1528 | label, i.e., the object has associated labels. 1529 | 1530 | This API is designed to be used in conjunction with other APIs and 1531 | elements on the web platform. In particular, postMessage() 1532 | and XMLHttpRequest (e.g., with an 1533 | overloaded send() method for LabeledObject 1534 | arguments). It SHOULD be exposed to both confined contexts and 1535 | unconfined contexts. 1536 | 1537 | A LabeledObject MUST have an internal protected 1538 | object, a confidentiality label, and an 1539 | integrity label. The interface is defined below. 1540 | 1541 |
1542 |     dictionary CILabel {
1543 |       Label? confidentiality;
1544 |       Label? integrity;
1545 |     };
1546 | 
1547 |     [Constructor(object obj, optional CILabel labels), Exposed=(Window, Worker), SecureContext]
1548 |     interface LabeledObject {
1549 |       readonly attribute Label confidentiality;
1550 |       readonly attribute Label integrity;
1551 | 
1552 |       [GetterThrows] readonly attribute object protectedObject;
1553 | 
1554 |       [Throws] LabeledObject clone(optional CILabel labels);
1555 |     };
1556 |     
1557 | 1558 |

Constructors

1559 |
1560 |
LabeledObject(obj, labels)
1561 |
1562 | 1563 | When invoking the LabeledObject() constructor, the user 1564 | agent MUST use an algorithm equivalent to the following: 1565 | 1566 | 1. Let obj clone be the result of obtaining a 1567 | structured clone of the obj argument. 1569 | 1570 | 2. Let labels be the labels 1572 | argument if it provided, otherwise an empty object with no members. 1573 | 1574 | 3. Let conf be the confidentiality member of labels, if it 1576 | is set. Otherwise, let conf be the current 1577 | confidentiality label. 1578 | 1579 | 4. Let int be the integrity member of labels, if it is set. 1581 | Otherwise, let int be the current integrity label. 1582 | 1583 | 5. Let canWrite be the result of invoking the write 1584 | check algorithm with the conf and int labels. 1585 | 1586 | 6. If canWrite is false, the constructor 1587 | MUST throw a SecurityError exception 1588 | and terminate this algorithm. 1589 | 1590 | 7. Else, the user agent MUST return a new 1591 | LabeledObject, with the protected object 1592 | set to obj clone, the confidentiality label set to 1593 | conf, and the integrity 1594 | label set to int. 1595 | 1596 |
1597 |
1598 | 1599 |
1600 | Because COWL enforces labels at context boundaries, there is 1601 | usually no reason to label an object and then use the labeled 1602 | object within the same context. LabeledObjects 1603 | are mainly useful for sending sensitive data to an untrusted 1604 | confined context, e.g., via cross-document messaging, as a 1605 | way to ensure that the data's confidentiality and integrity (as specified 1606 | by the labels) are respected by the untrusted context. Hence, the 1607 | LabeledObject() constructor only accepts objects that can be 1608 | structurally cloned. 1609 |
1610 | 1611 |

Attributes

1612 |
1613 |
confidentiality
1614 |
1615 | On getting, the user agent MUST return the LabeledObject's confidentiality label. 1617 |
1618 |
integrity
1619 |
1620 | On getting, the user agent MUST return the LabeledObject's integrity label. 1622 |
1623 |
protectedObject
1624 |
1625 | 1626 | ISSUE: should protectedObject be a promise? Otherwise we may be a bit too inflexible. 1627 | 1628 | On getting, the user agent MUST use an algorithm equivalent to 1629 | the following: 1630 | 1631 | 1. If the current settings object is not a secure 1632 | context, then the user agent MUST throw a 1633 | SecurityError exception and terminnate this algorithm. 1634 | 1635 | 2. Invoke the context tainting algorithm with the LabeledObject's confidentiality and integrity labels. 1637 | 1638 | 3. If the tainting algorithm raised an exception, re-throw the 1639 | exception and terminate this algorithm. 1640 | 1641 |
1642 | The tainting algorithm fails if code in a unconfined 1643 | context tries to read data that should confine the context. 1644 | To make it easier for developers to debug such cases, the user 1645 | agent SHOULD report a message to inform developers that they can 1646 | create cowl iframes wherein they can inspect such sensitive 1647 | objects instead. 1648 |
1649 | 1650 | 4. Else, return the LabeledObject's protected object. 1651 | 1652 |
1653 |
1654 | 1655 |
1656 | Note: the labels of a LabeledObject are 1657 | essentially public since code can always inspect the labels. 1658 | However, to inspect the internal, protected object, the 1659 | current context must be tainted according to the object's 1660 | labels. This ensures two things: 1661 | 1662 | * The context can't violate the confidentiality of the data (as 1663 | specified by the confidentiality label) by communicating 1664 | arbitrarily once it reads data labeled as such. 1665 | 1666 | * The context can't violate the integrity of entities more 1667 | trustworthy than the data. (The trustworthiness of the data is 1668 | specified by the integrity label.) In particular, once the 1669 | context reads the data and gets tainted, the rest of the 1670 | computation is restricted to writing to entities that are at 1671 | most as trustworthy as the data, since the read data may have 1672 | influenced the computation. 1673 |
1674 | 1675 |

Methods

1676 |
1677 |
clone(CILabel labels)
1678 |
1679 | 1680 | On invocation, the user agent MUST use an algorithm equivalent 1681 | to the following: 1682 | 1683 | 0. Let obj be the protected object of the 1684 | object on which the method was invoked. 1685 | 1686 | 1. Let conf be the confidentiality label of the 1687 | object on which the method was invoked. 1688 | 1689 | 2. Let int be the integrity label of the 1690 | object on which the method was invoked. 1691 | 1692 | 3. Let newConf be the confidentiality member of the labels argument, if it is 1695 | set. Otherwise, let newConf be conf. 1696 | 1697 | 4. Let newInt be the integrity member of the labels 1700 | parameter, if it is set. Otherwise, let newInt be 1701 | int. 1702 | 1703 | 5. Let privs be the internal privilege 1704 | label of the current context privileges. 1705 | 1706 | 6. If newConf.subsumes(conf, privs) 1707 | returns false or 1708 | if int.subsumes(newInt, privs) 1709 | returns false, the method 1710 | MUST throw a SecurityError exception and 1711 | terminate this algorithm. 1712 | 1713 | Note, these checks ensure that the new labels of the object are 1714 | at least as restricting as the original labels, taking into 1715 | consideration the privileges of the context. 1716 | 1717 | 7. Else, return a new LabeledObject, with the 1718 | protected object set to obj, the 1719 | confidentiality label set to newConf, and the 1720 | integrity label set to newInt. 1721 | 1722 |
1723 |
1724 | 1725 |

Examples

1726 | Below are several examples showing the usage of LabeledObjects. [[#examples-checker]] gives an 1728 | example of how LabeledObjects can be used to 1729 | confine third-party libraries (e.g., a password strength 1730 | checker). [[#labeledobject-xhr-send-example]] and 1731 | [[#labeledobject-xhr-receive-example]] show how LabeledObjects are used with the 1733 | XMLHttpRequest constructor. 1734 | 1735 |
1736 | The author of https://police.gov wishes to plot the 1737 | location of police cars on a map provided by 1738 | https://maps.biz without revealing the individual 1739 | car locations (to the map provider). After revealing the general area to 1740 | https://maps.biz, the author of 1741 | https://police.gov labels the police car coordinates and 1742 | sends them to the mapping service: 1743 | 1744 |

1745 | 
1746 |         // Fetch map for provided location and draw it
1747 |         mapsIframe.postMessage({ cmd: 'draw', location: ... }, mapsOrigin);
1748 | 
1749 |         var locations = ... // Array of police-car coordinates
1750 | 
1751 |         // Label the locations (as being sensitive to the police origin):
1752 |         var labeledLocations = new LabeledObject(locations,
1753 |                                      { confidentiality: new Label(window.location.origin) });
1754 | 
1755 |         // Send the labeled locations to the map iframe to plot them
1756 |         mapsIframe.postMessage({ cmd: 'plot', locations: labeledLocations }, mapsOrigin);
1757 |       
1758 | 1759 | When receiving a draw message, the author of 1760 | https://maps.biz navigates the iframe map (a nested 1761 | context) to draw the map; otherwise, it simply forwards messages 1762 | from the parent (e.g., plot, zoom, and 1763 | move). (This design ensures that only the innermost 1764 | iframe gets tainted.) 1765 | 1766 | The innermost map cowl iframe registers a handler, that, for 1767 | example, draws cars on top of map tiles: 1768 |

1769 |       window.addEventListener("message", function (event) {
1770 |         switch (event.data.cmd) {
1771 |           case 'plot':
1772 |             var coordinates = event.data.locations.protectedObject;
1773 |             coordinates.forEach(function (coordinate) {
1774 |               // add car to map at coordinate
1775 |             });
1776 |           case 'zoom': ...
1777 |           case 'move': ...
1778 |           ...
1779 |         };
1780 |       }, false);
1781 |       
1782 | 1783 | Note that before getting the first protectedObject, the iframe 1784 | can communicate arbitrarily, e.g., to fetch map tiles. But once 1785 | it inspects the confidential locations COWL confines the 1786 | code—it restricts it to only communicating with 1787 | https://police.gov. Importantly, it can keep 1788 | receiving messages from its parent context via 1789 | postMessage() to, for instance, move a car. 1790 |
1791 | 1792 |
1793 | The author of https://example.com wishes to ensure 1794 | that a particular JSON object conforms to a set of validation 1795 | filters before submitting it to a remote server. Consider, for 1796 | example, a form validator that checks if an email address is 1797 | valid. To this end, it labels the JSON and sends the labeled 1798 | object to a cowl iframe that performs the validation. 1799 | 1800 | The validation author inspects the object, but only endorses it 1801 | if it it is an email: 1802 |

1803 |       function handler(lObj) {
1804 |         if (isValidEmail(lObj.protectedObject)) {
1805 |           var origin = window.location.origin;
1806 |           // Endorse with application-specific label: ' OR app:isValidEmail'
1807 |           var endorsement = new Label(origin).or('app:isValidEmail');
1808 |           // Return a clone of the labeled object that is additionally
1809 |           // endorsed by the validator's (sub-)origin.
1810 |           return lObj.clone({ integrity: lObj.integrity.and(endorsement) });
1811 |         } else {
1812 |           return null;
1813 |         }
1814 |       }
1815 |       
1816 | The author of https://example.com can then pass the 1817 | endorsed object to other validators (e.g., a validator that checks emails 1818 | against a black-list) or end-point (e.g., a server), who can, in turn, 1819 | further endorse the object or verify the origins that have endorsed it. 1820 |
1821 | 1822 |
1823 | 1824 |
1825 | 1826 | 1827 | The Sec-COWL HTTP header field is 1828 | used to convey label metadata. 1829 | 1830 | Label metadata is either labeled context metadata 1831 | or labeled data metadata. 1832 | 1833 | Labeled context metadata encodes COWL state 1834 | information, including: 1835 | 1836 | * the serialized context confidentiality label given by the ctx-confidentiality directive, 1837 | * the serialized context integrity label given by the ctx-integrity directive, and 1838 | * the serialized context privilege's internal privilege label given by the ctx-privilege directive. 1839 | 1840 | Its ABNF is: 1841 | 1842 |
1843 |       ctx-metadata        = ctx-directive *( ";" [ ctx-directive ] )
1844 |       ctx-directive       = *WSP ctx-directive-name 1*WSP label-expression
1845 |       ctx-directive-name  = "ctx-confidentiality" / "ctx-integrity" / "ctx-privilege"
1846 |     
1847 | 1848 | Labeled data metadata is used to convey the 1849 | confidentiality and integrity labels of an HTTP request or response, using 1850 | the data-confidentiality and 1851 | data-integrity directives. Its ABNF is: 1852 | 1853 |
1854 |       data-metadata       = data-directive *( ";" [ data-directive ] )
1855 |       data-directive      = *WSP data-directive-name 1*WSP label-expression
1856 |       data-directive-name = "data-confidentiality" / "data-integrity"
1857 |     
1858 | 1859 | The ABNF for serialized labels is: 1860 | 1861 |
1862 |       label-expression   = empty-label / and-expression / or-expression / principal-expression
1863 |       and-expression     = *WSP "(" or-expression *WSP ")" *( 1*WSP "AND" WSP and-expression )
1864 |       or-expression      = *WSP principal-expression *( 1*WSP "OR" WSP or-expression ) 
1865 |       empty-label        = "'none'"
1866 |     
1867 | 1868 | Note, the ABNF grammar limits the expression of labels to conjunctions of 1869 | disjunctions, even though the JavaScript API allows AND's and OR's to be 1870 | used interchangably. This is done to simplify auditing of declarative 1871 | (header) labels, but may be changed in future versions to be more flexible. 1872 | 1873 | The ABNF for serialized principals is: 1874 |
1875 |       principal-expression        = origin-principal-expression 
1876 |                                   / app-principal-expression 
1877 |                                   / unique-principal-expression
1878 |       origin-principal-expression = "'self'" / host-source
1879 |       app-principal-expression    = "app:" 1*( ALPHA / DIGIT / "-" )
1880 |       unique-principal-expression = "unique:" UUID
1881 |     
1882 | 1883 | 1884 | The parsing algorithms for label metadata are given in 1885 | [[#parse-labeled-metadata-data]] and [[#parse-labeled-metadata-ctx]]. 1886 | 1887 |
1888 |

Sec-COWL in HTTP Requests

1889 | 1890 | The ABNF for Sec-COWL HTTP requests is: 1891 | 1892 |
1893 |         "Sec-COWL:" ( ctx-metadata [ "," data-metadata ] ) / ( data-metadata [ "," ctx-metadata ] )
1894 |       
1895 | 1896 | The user agent MUST send a header field named Sec-COWL 1897 | along with requests if confinement mode is enabled and calling 1898 | determine request's referrer algorithm does not return 1899 | none. The value of this header MUST contain the labeled 1900 | context metadata of the context that performed the request. This 1901 | labeled context metadata MUST include the current context 1902 | confidentiality label, context integrity label, and 1903 | context privileges. The user agent MAY send another header with 1904 | this field name whose value is labeled data metadata (e.g., when 1905 | sending labeled objects with XMLHttpRequest). 1906 | 1907 | Note, according to [[!RFC7231]], the user agent MAY combine 1908 | multiple header field values into a single, comma-separated 1909 | value. 1910 | 1911 | 1912 |
1913 | Request header from a https://a.com page that has 1914 | read data sensitive to https://b.com. 1915 |

1916 |         Sec-COWL: ctx-confidentiality https://b.com;
1917 |                   ctx-integrity 'none';
1918 |                   ctx-privilege https://a.com
1919 |         
1920 |
1921 |
1922 | A request sent from a public, untrustworthy 1923 | https://university.edu context that owns a 1924 | delegated privilege and a FreshPrivilege(): 1925 | 1926 |

1927 |         Sec-COWL: ctx-confidentiality 'none';
1928 |                   ctx-integrity 'none';
1929 |                   ctx-privilege (https://university.edu OR app:user1) AND (unique:a0281e1f-8412-4068-a7ed-e3f234d7fd5a)
1930 |         
1931 |
1932 | 1933 | When processing a request, a server SHOULD only use the first 1934 | Sec-COWL header that contains a ctx-metadata 1935 | directive to retrieve the labeled context metadata. 1936 | Similarly, a server SHOULD only use the first Sec-COWL 1937 | header that contains a data-metadata directive to retrieve 1938 | the labeled data metadata of the request. 1939 | 1940 |
1941 | 1942 |
1943 |

Sec-COWL in HTTP Responses

1944 | 1945 | The ABNF for Sec-COWL HTTP responses is: 1946 |
1947 |         "Sec-COWL:" ctx-metadata / data-metadata
1948 |       
1949 | 1950 | The header value may contain labeled context metadata 1951 | which can be used to set the initial COWL state of a 1952 | context; or it may contain labeled data metadata which 1953 | specifies the sensitivity of the response (which COWL then uses 1954 | to determine whether or not to block the response). 1955 | 1956 |
1957 | An author may wish to specify that the 1958 | https://university.edu/~user1 page should run with 1959 | a delegated privilege—namely, 1960 | Label("https://university.edu/").or("app:user1")—from 1961 | the start: 1962 |

1963 |         Sec-COWL: ctx-privilege 'self' OR app:user1;
1964 |         
1965 |
1966 |
1967 | The author of https://a.com may wish to respond to a 1968 | request with data that is sensitive to both https://a.com and 1969 | https://b.com, while simultaneously indicating that 1970 | it endorses the response data: 1971 |

1972 |         Sec-COWL: data-confidentiality ('self') AND (https://b.com);
1973 |                   data-integrity 'self'
1974 |         
1975 | COWL blocks the response unless the current context's labels 1976 | are at least as restricting. 1977 |
1978 | 1979 | To process this header, the user agent MUST use the 1980 | Process response to request as COWL 1981 | algorithm when performing a fetch, as described in [[#modifications-fetch]]. 1982 |
1983 |
1984 | 1985 |
1986 |

Extensions to XMLHttpRequest

1987 | 1988 | ISSUE: should we disable XHR and just force developers to use fetch? 1989 | 1990 | The XMLHttpRequest specification SHOULD contain the 1991 | modifications described below to enable the rest of this 1992 | specification's work [[XHR]]. 1993 | 1994 |

Sending labeled objects

1995 | To allow authors to send labeled objects to a remote 1996 | server, this specification extends the 1997 | XMLHttpRequest interface with an overloaded 1998 | send() method: 1999 | 2000 |
2001 |         partial interface XMLHttpRequest {
2002 |           void send(LabeledObject lobj);
2003 |         };
2004 |       
2005 | The send(lobj) method MUST use an 2007 | algorithm that is equivalent to the following: 2008 | 2009 | 1. Let obj be the protected object of the 2010 | lobj argument. 2011 | 2012 | 2. Let conf be the confidentiality label of the lobj argument. 2014 | 2015 | 3. Let int be the integrity label of the lobj argument. 2017 | 2018 | 4. Let privs be the current context privileges. 2019 | 2020 | 5. Let remoteConf be the label 2021 | returned by the Label(principal) constructor 2022 | called with the stringified origin 2023 | url 2024 | associated with the request. 2025 | 2026 | 6. If remoteConf.subsumes(conf, 2027 | privs) returns false, throw 2028 | a SecurityError and terminate this algorithm. 2029 | 2030 | The user agent SHOULD report a warning that the context 2031 | attempted to leak data to a remote server. 2032 | 2033 | 7. Let json be a new JSON object with the 2034 | following entries: 2035 | 2036 | * "confidentiality" set to the serialized conf. 2037 | * "integrity" set to the the serialized int. 2038 | * "object" set to obj. 2039 | 2040 | 8. Set the Content-Type header to `application/labeled-json`. 2041 | 2042 | 9. Append a header named Sec-COWL to the 2043 | author request headers associated with the object this 2044 | methods was called on. The value of the 2045 | Sec-COWL header MUST be labeled data 2046 | metadata containing the confidentiality and integrity 2047 | labels of the lobj argument. 2048 | 2049 | 10. Invoke the send() 2050 | method on the object this method was called on with 2051 | json as an argument. 2052 | 2053 | Note, that send() 2054 | throws an exception if obj cannot be serialized. 2055 | User agents MUST ensure that all protected objects can be 2056 | serialized at the time of creating LabeledObjects. 2057 | 2058 |
2059 | This algorithm does not check if the integrity label of 2060 | the object subsumes the server's integrity label. It is 2061 | the server's responsibility to ensure that untrustworthy 2062 | data does not affect its computation in an unsafe way. 2063 | Indeed, the only reason for checking the confidentiality 2064 | labels is because the user agent has no way to ensure that 2065 | the server will respect the confidentiality of the data. 2066 |
2067 | 2068 |
Examples
2069 |
2070 | Author of https://example.com sends JSON object 2071 | endorsed by https://validator.com: 2072 |

2073 |       // Suppose that labeledObject is a public, high-integrity value:
2074 |       labeledObject.confidentiality.toString() === "'none'";
2075 |       labeledObject.integrity.toString() === "https://validator.com";
2076 | 
2077 |       // Create an XHR request:
2078 |       var req = new XMLHttpRequest()
2079 |       req.open("POST", "https://example.com/");
2080 | 
2081 |       // Send the labeled object:
2082 |       req.send(labeledObject);
2083 |       
2084 | Assuming the context has a default COWL state, 2085 | send() would make an HTTP request of the form: 2086 | 2087 |

2088 |       Sec-COWL: ctx-confidentiality 'none';
2089 |                 ctx-integrity 'none';
2090 |                 ctx-privilege https://example.com
2091 |       Sec-COWL: data-confidentiality 'none';
2092 |                 data-integrity https://validator.com
2093 |       Content-Type: application/labeled-json;
2094 | 
2095 |       {
2096 |         "confidentiality": "'none'",
2097 |         "integrity": "https://validator.com",
2098 |         "object": ... JSON object ...
2099 |       }
2100 |       
2101 | The server can then verify the integrity label of the request 2102 | and ensure that, if the user agent is conformant, the data was 2103 | endorsed by https://validator.com. 2104 |
2105 | 2106 |

Receiving labeled objects

2107 | To allow authors to receive labeled objects from remote 2108 | servers, the XMLHttpRequest specification SHOULD contain the 2109 | following modifications [[XHR]]: 2110 | 2111 | 1. The XMLHttpRequestResponseType 2112 | enumeration is extended with a new response type: 2113 | 2114 |
2115 |            enum XMLHttpRequestResponseType {
2116 |              // ... existing response types ...
2117 |              "labeled-json"
2118 |            };
2119 |          
2120 | 2121 | 2. The Response 2122 | body section of the specification is modified to add: 2123 | 2124 | 1. An XMLHttpRequest has associated 2125 | response LabeledObject object. 2126 | 2127 | 2. A labeled JSON response is the return value 2128 | of these steps: 2129 | 2130 | 1. If the response LabeledObject object is non-null, return it. 2131 | 2132 | 2. If responseType is not 2133 | "labeled-json" or the final MIME 2134 | type is not application/labeled-json, return null. 2135 | 2136 | 3. Let bytes be the response's body. 2137 | 2138 | 3. If bytes is null, return null. 2139 | 2140 | 4. Let JSON text be the result of running 2141 | utf-8 decode on byte stream bytes. 2142 | 2143 | 5. Let JSON object be the result of invoking the initial 2144 | value of the parse property of the 2145 | JSON object, with 2146 | JSON text as its only argument. If that threw an 2147 | exception, return null. [[!ECMA-262]] 2148 | 2149 | 6. If the JSON object is missing any of 2150 | the three entries: "object", 2151 | "confidentiality", or 2152 | "integrity" return null. 2153 | 2154 | 7. Let protected object be the value of the "object" entry. 2155 | 2156 | 8. Let self be the 2157 | url 2158 | associated with the response. 2159 | 2160 | 9. Let conf be the label returned by invoking the 2161 | [[#parse-label-expression]] algorithm with the 2162 | "confidentiality" entry of the JSON object 2163 | and self. If parsing failed, return null. 2164 | 2165 | 10. Let int be the label returned by invoking the 2166 | [[#parse-label-expression]] algorithm with the 2167 | "integrity" entry of the JSON object 2168 | and self. If parsing failed, return null. 2169 | 2170 | 11. Let responseInt be the label 2171 | returned by the Label(principal) constructor 2172 | called with self. 2173 | 2174 | 12. If responseInt does not subsume 2175 | int, return null. 2176 | 2177 | The user agent SHOULD report a warning message indicating that 2178 | the server provided an integrity label that it is not allowed 2179 | to provide. 2180 | 2181 | 13. Set the response LabeledObject object to a newly 2182 | created LabeledObject whose 2183 | protected object is protected object, 2184 | whose confidentiality label is conf, and 2185 | whose integrity label is int. 2186 | 2187 | 14. Return the response LabeledObject object. 2188 | 2189 | 3. Modify the response attribute by 2190 | adding the following clause to step 2 of the 2191 | ↪ Otherwise clause: 2192 | 2193 | ↪ If responseType is "labeled-json" 2194 |

Return the labeled JSON response.

2195 | 2196 | 4. Modify step 12 of the open() method by adding the 2197 | following sub-step: 2198 | 2199 | * Set response LabeledObject object to null. 2200 | 2201 | 2202 |
Examples
2203 |
2204 | [[#examples-mashup]] gives an example of a mashup scenario 2205 | wherein the data provider uses the Sec-COWL HTTP response header to 2207 | ensure that the mashup integrator can only read the HTTP 2208 | response if it is sufficiently confined. A more permissive 2209 | approach is to send a labeled JSON response. 2210 | 2211 | Specifically, the server operator of 2212 | https://provider.com uses a CORS response header to 2213 | send https://mashup.com a labeled JSON object. To 2214 | ensure that the data is protected it sets the 2215 | Content-Type response header value to 2216 | `application/labeled-json` and sets the labels 2217 | appropriately: 2218 | 2219 |

2220 |        Access-Control-Allow-Origin: https://mashup.com
2221 |        Content-Type: application/labeled-json;
2222 | 
2223 |        {
2224 |          "confidentiality": "'self'",
2225 |          "integrity": "'self'",
2226 |          "object": ... JSON object ...
2227 |        }
2228 |       
2229 | 2230 | The confidentiality label specifies that the object is 2231 | confidential to https://provider.com and should 2232 | not be disseminated arbitrarily. 2233 | 2234 | Note, the server operator can also set a Sec-COWL response header if it 2236 | wished to ensure that the context is already confined according to some 2237 | label. 2238 | 2239 | The author of https://mashup.com can read such 2240 | labeled responses by simply setting the responseType accordingly: 2242 |

2243 | 
2244 |       // Create an XHR request to get the data:
2245 |       var req = new XMLHttpRequest()
2246 |       req.open("GET", "https://provider.com/apis/...");
2247 |       req.responseType = "labeled-json";
2248 |       req.onload = function (e) {
2249 |         var labeledObject = req.response; // is a LabeledObject
2250 | 
2251 |         // At this point, the context is still untainted, but:
2252 |         labeledObject.confidentiality.toString() === "https://provider.com";
2253 |         labeledObject.integrity.toString() ===  "https://provider.com";
2254 |       };
2255 |       req.send();
2256 |       
2257 | 2258 | Here, COWL sets the response to a new LabeledObject, but does not taint the 2260 | context with the response label. Indeed the 2261 | https://mashup.com integrator can perform many 2262 | other requests to different origins. Only when the 2263 | protected objects of these labeled objects are 2264 | used will COWL taint the context and impose the label 2265 | restrictions. 2266 |
2267 |
2268 | An image provider can serve "read-once" images by labeling them 2269 | with a unique origin when reply to an HTTP request: 2270 |

2271 |        Access-Control-Allow-Origin: *
2272 |        Content-Type: application/labeled-json;
2273 | 
2274 |        {
2275 |          "confidentiality": app:too-secret,
2276 |          "integrity": 'none',
2277 |          "object": ... base64-encoded image ...
2278 |        }
2279 |       
2280 | Once the receiver inspects the protectedObject 2281 | of the response, COWL taints the context and ensures that 2282 | it cannot communicate with anybody. 2283 | 2284 | Note, to read the protected object, one must do so in a confined context 2285 | since reading such a sensitive data would prevent code from communicating 2286 | arbitrarily thereafter. 2287 |
2288 | 2289 |
2290 | 2291 |
2292 |

Confinement Enforcement

2293 | 2294 | This sub-section is non-normative 2295 | 2296 | To enforce confinement, COWL ensures that code in a 2297 | browsing context cannot send data (e.g., via cross-document 2298 | messaging or by performing a fetch) to contexts or 2299 | servers that do not preserve the confidentiality of the data. 2300 | Similarly, COWL ensures that a context cannot receive data from a 2301 | context or server that is less trustworthy. 2302 | 2303 |

Modifications to Fetch

2304 | 2305 | The Fetch specification SHOULD contain the following 2306 | modifications in order to enable the rest of this 2307 | specification's work [[FETCH]]: 2308 | 2309 | 1. Perform the following step between step 5 and 6 in the 2310 | "main fetch" algorithm: 2311 | 2312 | 1. If should fetching request be blocked as 2313 | COWL returns blocked, set 2314 | response to a network error. 2315 | 2316 | 2. Perform the following step between step 13 and 14 in the 2317 | "main fetch" algorithm: 2318 | 2319 | 1. If process 2320 | response to request as COWL 2321 | returns blocked, set response 2322 | to a network error. 2323 | 2324 |

Modifications to Web Messaging

2325 | 2326 | The Web Messaging specification SHOULD contain the following 2327 | modifications in order to enable the rest of this 2328 | specification's work [[WEBMESSAGING]]: 2329 | 2330 | 1. Perform the following step between step 9 and 10 in the 2331 | posting messages algorithm: 2332 | 2333 | 1. Let conf be the current context's effective 2334 | confidentiality label. 2335 | 2336 | 2. Let int be the current context's effective 2337 | integrity label. 2338 | 2339 | 3. Let dstState be the COWL state 2340 | associated with the 2341 | Document of the 2342 | Window object on which the 2343 | method was invoked. 2344 | 2345 | 4. Let dstConf be the Label returned 2346 | by the label upgrade algorithm when invoked with the 2347 | dstState context confidentiality label and 2348 | context privilege. 2349 | 2350 | Note, in using the label upgrade COWL flexibly assumes 2351 | that receivers wish to receive data potentially sensitive to 2352 | their origin (really data they can declassify with their 2353 | privileges). 2354 | 2355 | 5. Let dstInt be the dstState context 2356 | integrity label. 2357 | 2358 | 6. If dstConf does not subsume 2359 | conf or if int does not 2360 | subsume dstInt, then abort the 2361 | remaining steps silently. 2362 | 2363 | 2. Perform the following step between step 9 and 10 in the 2364 | MessagePort postMessage() method: 2365 | 2366 | 1. Let conf be the current context's effective 2367 | confidentiality label. 2368 | 2369 | 2. Let int be the current context's effective 2370 | integrity label. 2371 | 2372 | 3. Let dstState be the COWL state 2373 | associated with the owner of the target 2374 | port the Message Port 2375 | postMessage() was called on. 2376 | 2377 | 4. Else, let dstConf be the Label 2378 | returned by the label upgrade algorithm when invoked with 2379 | the dstState context confidentiality label and 2380 | context privilege. 2381 | 2382 | Note, in using the label upgrade COWL flexibly assumes 2383 | that receivers wish to receive data potentially sensitive to 2384 | their origin (really data they can declassify with their 2385 | privileges). 2386 | 2387 | 5. Let dstInt be the dstState context 2388 | integrity label. 2389 | 2390 | 6. If dstConf does not subsume 2391 | conf or if int does not 2392 | subsume dstInt, then abort the 2393 | remaining steps. 2394 | 2395 |

Modifications to HTML5

2396 | 2397 | The HTML5 specification SHOULD contain the following 2398 | modifications in order to enable the rest of this 2399 | specification's work [[HTML5]]: 2400 | 2401 | 1. To allow authors to create confined contexts this this 2402 | specification extends the HTMLIFrameElement 2403 | interface with a new cowl attribute: 2404 | 2405 |
2406 |                 partial interface HTMLIFrameElement {
2407 |                   attribute boolean cowl;
2408 |                 };
2409 |               
2410 | 2411 | The cowl attribute, when 2412 | specified, enables a set of extra restrictions on any content 2413 | hosted by the iframe, much like the sandbox attribute. 2414 | We call an iframe with the cowl attribute set 2415 | a cowl iframe. 2416 | 2417 | When an iframe element with a cowl 2418 | attribute has its nested browsing context created (before 2419 | the initial about:blank Document is created), the 2420 | user agent MUST enable the context's confinement mode. The 2421 | iframe element's cowl attribute may not 2422 | be set or changed after this point. 2423 | 2424 | ISSUE: what's the best way to deal with authors modifying the cowl attribute? 2425 | 2426 | 1. When confinement mode is enabled the user agent MUST 2427 | ensure that content cannot access other content from the 2428 | same origin (e.g., using an iframe's 2429 | contentDocument) or use 2430 | otherwise unsafe APIs that would violate label restrictions. 2431 | 2432 | Specifically, if a browsing context's confinement 2433 | mode is enabled the user agent MUST set the following flags 2434 | of the context's active sandboxing flag set: 2435 | 2436 | * The sandboxed document.domain browsing context flag. 2437 | 2438 | * The sandboxed origin browsing context flag. 2439 | 2440 | Note, this flag is used to ensure that the browsing context cannot 2441 | directly access content of the same origin. It also prevents 2442 | script from reading from or writing to the 2443 | document.cookie IDL attribute, and blocks access 2444 | to localStorage. [[WEBSTORAGE]] 2445 | 2446 | ISSUE: is there a better way for us to just disallow direct DOM 2447 | access and preserve same-origin? 2448 | 2449 | * The sandboxed navigation browsing context flag. 2450 | 2451 | * The sandboxed auxiliary navigation browsing context flag. 2452 | 2453 | * The sandboxed top-level navigation browsing context flag. 2454 | 2455 | In addition to these flags, the user agent MUST set a 2456 | container policy of the cowl iframe that contains 2457 | the confined context that disables the following features: 2458 | 2459 | * Workers: Web Workers, Shared Workers, Service Workers 2460 | 2461 | * Communication: Web Sockets, Server-sent events, 2462 | channel messaging, broadcast channels and WebRTC 2463 | 2464 | * Storage: document.cookie, localStorage, IndexedDB 2465 | 2466 | 2467 |
2468 | 2469 |
2470 | 2471 | 2480 |
2481 |

Algorithms

2482 |
2483 |

Label Normal Form Reduction

2484 | The label normal form reduction algorithm takes a 2485 | label argument and produces a Label value 2486 | according to the following steps: 2487 | 2488 | 1. Let lset be the label set of an empty label. 2489 | 2490 | 2. For each disjunction set dset in the 2491 | label set of label: 2492 | 2493 | 1. If there is no disjunction set in lset 2494 | that is a subset of dset, then: 2495 | 2496 | 1. Remove every disjunction set in lset that 2497 | dset is a subset of. 2498 | 2499 | 2. Add dset to lset. 2500 | 2501 | 3. Return a newly created Label whose label set is lset. 2502 | 2503 | Note, this algorithms assumes that disjunction sets and 2504 | label sets do not have duplicate elements, much like 2505 | mathematical sets. 2506 | 2507 |
2508 | The Label API uses this algorithm to ensure that labels 2509 | don't have redundant information. Consider for example, the 2510 | following labels: 2511 |

2512 |         var a    = Label("https://a.com");  // https://a.com
2513 |         var aORb = Label("https://a.com").or("https://b.com"); // https://a.com OR https://b.com
2514 |         var a2   = a.and(aORb); // (https://a.com) AND (https://a.com OR https://b.com) ≡ https://a.com
2515 |       
2516 | 2517 | The label a2 is equivalent to a (since 2518 | a.subsumes(aORb)): 2519 | 2520 |

2521 |         a2.toString() === "https://a.com";
2522 |         a2.equals(a);
2523 |       
2524 |
2525 |
2526 | 2527 |
2528 |

Label Subsumption

2529 | The label subsumption algorithm takes a two labels 2530 | A and B and produces a boolean according to 2531 | these steps: 2532 | 2533 | 1. If, for each disjunction set 2534 | b in the label set of B there is 2535 | disjunction set a in the label set of 2536 | A such that a is a subset of 2537 | b, return true. 2538 | 2539 | 2. Else, return false. 2540 | 2541 | Note, when interpreting labels as mathematical formulae, label 2542 | subsumption is logical implication: A 2543 | subsumes B is equivalent as A 2544 | implies B, i.e, AB. 2545 |
2546 | 2547 |
2548 |

Label Downgrade

2549 | The label downgrade algorithm takes a label label and 2550 | a privilege priv, and returns the least restricting label 2551 | according to the following steps: 2552 | 2553 | 1. Let privLabel be the internal privilege label 2554 | of priv. 2555 | 2556 | 2. Let lset be the label set of an empty label. 2557 | 2558 | 2. For each disjunction set dset in the 2559 | label set of label: 2560 | 2561 | 1. Let cur be a newly created Label 2562 | whose label set is dset. 2563 | 2564 | 2. If privLabel does not subsume 2565 | cur, add dset to lset. 2566 | 2567 | 3. Return a newly created Label whose label set is lset. 2568 | 2569 | Note, label downgrade removes every disjunction set 2570 | permitted by priv. This is used to safely declassify 2571 | data labeled label. 2572 |
2573 | 2574 |
2575 |

Label Upgrade

2576 | The label upgrade algorithm takes a label label and a 2577 | privilege priv, and returns the most permissive label according 2578 | to the following steps: 2579 | 2580 | 1. Let privLabel be the internal privilege label 2581 | of priv. 2582 | 2583 | 2. Return label.and(privLabel). 2584 | 2585 | Note, label upgrade is the dual of label downgrade. 2586 | This can be used to safely endorse data labeled label 2587 | (and thus potentially already endorsed). 2588 |
2589 | 2590 |
2591 |

Context Tainting

2592 | The context tainting algorithm takes a two labels, 2593 | confidentiality and integrity, and updates the 2594 | context labels to allow for reading data labeled with these labels. 2595 | If the context is an unconfined context and tainting the 2596 | context would change its COWL state, the algorithm throws a 2597 | SecurityError exception. The user agent MUST use an algorithm 2598 | whose behavior is as follows: 2599 | 2600 | 1. Let currentConf be the current context 2601 | confidentiality label. 2602 | 2603 | 2. Let currentInt be the current context 2604 | integrity label. 2605 | 2606 | 2. Let newConf be the Label returned by the by 2607 | the label downgrade algorithm when invoked with 2608 | currentConf.and(confidentiality) 2609 | and current privilege. 2610 | 2611 | 3. Let newInt be the Label returned by the 2612 | label downgrade algorithm when invoked with 2613 | currentInt.or(integrity) and current 2614 | privilege. 2615 | 2616 | 4. If the current context is an unconfined context and 2617 | either newConf or newInt are not the 2618 | empty label, throw a SecurityError exception 2619 | and terminate this algorithm. 2620 | 2621 | 5. Set the context confidentiality label to newConf. 2622 | 2623 | 6. Set the context integrity label to newInt. 2624 | 2625 |
2626 | 2627 |
2628 |

Write Check

2629 | The write check algorithm takes two labels, 2630 | objConf and objInt, and returns 2631 | true if the current context is allowed to write to 2632 | (or create) an entity labeled as such; otherwise, it returns 2633 | false. The user agent MUST use an algorithm whose 2634 | behavior is as follows: 2635 | 2636 | 1. Let currentConf be the current context's effective 2637 | confidentiality label. 2638 | 2639 | 2. Let currentInt be the current context's effective 2640 | integrity label. 2641 | 2642 | 3. If objConf does not subsume 2643 | currentConf or if currentInt does not 2644 | subsume objInt, return false. 2645 | 2646 | 7. Else, return true. 2647 |
2648 | 2649 |
2650 |

Structured Cloning

2651 | When a user agent is required to obtain a structured clone 2652 | of an object whose type is defined in this document, it MUST use 2653 | an algorithm whose behavior is as follows: 2654 | 2655 | 1. Let input be the value being cloned. 2656 | 2657 | 2. If input is a Label object, let 2658 | output be a newly constructed Label 2659 | object with the same label set as that of 2660 | input. 2661 | 2662 | 3. If input is a Privilege object, then: 2663 | 2664 | 1. If the input's internal privilege label 2665 | subsumes any label whose label set is a singleton 2666 | origin principal, let output be null. 2667 | 2668 | 2. Else, let output be a newly constructed 2669 | Privilege object with the same internal privilege 2670 | label as that of input. 2671 | 2672 | 2673 |
2674 | To prevent attacks that launder default privileges, the current 2675 | version of COWL only allows transferring weaker delegated 2676 | privileges or privileges constructed with FreshPrivilege(). 2677 | The first step above ensures this by setting the output to 2678 | null if the privilege subsumes any privilege 2679 | corresponding to an origin principal. 2680 |
2681 | 2682 | 4. If input is a LabeledObject object, let 2683 | output be a newly constructed 2684 | LabeledObject object whose 2685 | confidentiality and integrity labels are the same as that of 2686 | input, and whose internal protected object is 2687 | a structured clone of the protected object of the 2688 | input LabeledObject. 2689 | 2690 | 5. Return output. 2691 | 2692 | Note, cross-context messaging constructs such as 2693 | postMessage() use the structured clone algorithm 2694 | (e.g., see the internal structured cloning algorithm). 2695 | This algorithm is used to allow authors to transfer COWL 2696 | object, such as LabeledObjects, to other contexts. 2697 |
2698 | 2699 |
2700 |

2701 | Should fetching request be blocked as COWL? 2702 |

2703 |
2704 | Note: this algorithm is used to determine whether a request should 2705 | be entirely blocked, because it may potentially leak sensitive 2706 | data to an unauthorized server. 2707 |
2708 | 2709 | Given a Request request, a user agent determines 2710 | whether the Request request should proceed or 2711 | not via the following algorithm: 2712 | 2713 | 1. Let context be the client 2714 | associated with the request. 2715 | 2716 | 2. If context is null, let context be the 2717 | incumbent settings object. 2718 | 2719 | Note, the client associated with the 2720 | request is null when navigating, so we use 2721 | the incumbent settings object to get the COWL 2722 | state of the browsing context that initiated the request. 2723 | 2724 | 2. Let state be the COWL state retrieved via the 2725 | environment settings object context. 2726 | 2727 | 3. If the state confinement mode is not 2728 | enabled, return allowed and terminate this 2729 | algorithm. 2730 | 2731 | 4. Let conf be the state effective confidentiality label. 2732 | 2733 | 6. Let dstConf be the Label created by invoking 2734 | the Label(principal) constructor with the stringified 2735 | origin of the 2736 | url 2737 | associated with the request. 2738 | 2739 | 7. If dstConf subsumes conf, return 2740 | allowed. 2741 | 2742 | 8. Else, return blocked. 2743 | 2744 | The user agent SHOULD report a warning that the context 2745 | attempted to leak data to a remote server. 2746 | 2747 | Note, the integrity label of the current context is not used in 2748 | this algorithm since, conceptually, the integrity label of a 2749 | server is the empty label and, thus, always subsumed. 2750 | Server operators SHOULD check the 2751 | Sec-COWL request header 2752 | to ensure untrustworthy data does not affect the computation in an 2753 | unsafe way. 2754 |
2755 | 2756 |
2757 |

2758 | Process response to request as COWL 2759 |

2760 |
2761 | If a request proceeds, we 2762 | still might want to block the response based on the labeled 2763 | data metadata of the response. For example, if the 2764 | current confidentiality label does not subsume the 2765 | confidentiality label of the response, the user agent MUST block 2766 | the response since it could otherwise violate the confidentiality 2767 | of the response data. (The dual holds for integrity.) This 2768 | algorithm is used to make the determination of whether or not a 2769 | response is blocked. 2770 | 2771 | This algorithm is also used to set the COWL state for 2772 | new documents and Workers according to the server-supplied 2773 | labeled context metadata. 2774 |
2775 | 2776 | Given a Request request and Response 2777 | response, a user agent determines 2778 | whether the response should be returned via the following algorithm: 2779 | 2780 | 1. If the response's header list has no 2781 | header whose name is 2782 | Sec-COWL, return allowed and 2783 | terminate this algorithm. 2784 | 2785 | 2. Let destination be the request's destination. 2786 | 2787 | 3. Let context be the client 2788 | associated with the request. 2789 | 2790 | 4. If context is null, let context be the 2791 | incumbent settings object. 2792 | 2793 | Note, the client associated with the 2794 | request is null when navigating, so we use 2795 | the incumbent settings object to get or set the COWL 2796 | state of the browsing context that initiated the request. 2797 | 2798 | 5. Let state be the COWL state retrieved via the 2799 | environment settings object context. 2800 | 2801 | 6. Let metadata be 2802 | the first header whose name 2803 | is Sec-COWL in the response's header list. 2804 | 2805 | 7. If destination is "document", 2806 | "worker" or "serviceworker": 2807 | 2808 | 0. Let self be the serialization of the 2809 | origin retrieved via the 2810 | environment settings object context. 2811 | 2812 | 1. Let conf, int, priv be the 2813 | result of calling the parse labeled context 2814 | metadata algorithm with metadata and self. 2815 | 2816 | 2. If either conf, int, or 2817 | priv are null, return blocked. 2818 | 2819 | The user agent SHOULD report a warning that the server supplied a 2820 | malformed Sec-COWL header. 2821 | 2822 | 3. Else: 2823 | 2824 | 1. If context is an unconfined context, 2825 | return blocked and terminate this algorithm. 2826 | 2827 | The user agent SHOULD report a warning that the application 2828 | attempted to embed confined content outside a cowl iframe. 2829 | 2830 | 2. If priv is not a delegated privilege of the 2831 | state context privilege, return 2832 | blocked and terminate this algorithm. 2833 | 2834 | The user agent SHOULD report a warning that the server supplied 2835 | a privilege that it is not trusted for. 2836 | 2837 | 3. If the state effective integrity label does 2838 | not subsume int, return blocked 2839 | and terminate this algorithm. 2840 | 2841 | The user agent SHOULD report a warning that the server supplied 2842 | an integrity label that it is not trusted for. 2843 | 2844 | 4. Set the state context confidentiality label 2845 | to conf. 2846 | 2847 | 5. Set the state context integrity label 2848 | to int. 2849 | 2850 | Note, by performing the label subsumption check (step 3 2851 | above) before setting the context privilege (next step), 2852 | the context integrity label can be upgraded from the 2853 | empty label, while allowing the context privilege 2854 | to also be dropped. 2855 | 2856 | 6. Set the state context privilege to 2857 | priv. 2858 | 2859 | 7. Return allowed. 2860 | 2861 | 8. Else: 2862 | 2863 | 1. Let self be the origin of the 2864 | url 2865 | associated with the response. 2866 | 2867 | 2. Let conf and int be the 2868 | results of calling the parse labeled data metadata 2869 | with metadata and self. 2870 | 2871 | 3. If either conf or int is null, 2872 | return blocked and terminate this algorithm. 2873 | 2874 | The user agent SHOULD report a warning that the server supplied a 2875 | malformed Sec-COWL header. 2876 | 2877 | 4. Let effConf be the Label returned by the 2878 | label downgrade algorithm when invoked with conf and 2879 | the state current privilege. 2880 | 2881 | Note, effConf is the effective confidentiality label of 2882 | the response—COWL flexibly declassifies responses (as much as 2883 | the current privilege permits). 2884 | 2885 | 5. If the state context confidentiality 2886 | label subsumes effConf and int 2887 | subsumes the state effective integrity 2888 | label, return allowed. 2889 | 2890 | 6. Else, return blocked. 2891 | 2892 | Note, COWL conservatively blocks a response that is 2893 | potentially more confidential or less trustworthy than the 2894 | context making the request. In future versions of COWL, 2895 | certain responses (e.g., images) which are only not as 2896 | trustworthy as the context integrity label 2897 | may be allowed by the user agent. 2898 |
2899 | 2900 |
2901 |

2902 | Parse labeled data metadata 2903 |

2904 | 2905 | To parse labeled data metadata metadata for 2906 | origin self, the user agent MUST use an algorithm 2907 | equivalent to the following: 2908 | 2909 | 1. Let conf be null. 2910 | 2911 | 2. Let int be null. 2912 | 2913 | 3. For each non-empty token returned by 2914 | strictly splitting 2915 | the string metadata on the character U+003B SEMICOLON (;): 2916 | 2917 | 1. Skip whitespace. 2918 | 2919 | 2. Collect a sequence of characters that are 2920 | not space characters. The collected characters 2921 | are the directive name. 2922 | 2923 | 3. If there are characters remaining in token, 2924 | skip ahead exactly one character (which must be a 2925 | space character). 2926 | 2927 | 4. The remaining characters in token (if any) are 2928 | the directive value. 2929 | 2930 | 5. Let label value be the label returned by 2931 | invoking the parse label-expression algorithm with the 2932 | directive value and self. If parsing 2933 | failed, terminate the rest of these sub-steps. 2934 | 2935 | The user agent SHOULD report a warning that the server provided a 2936 | malformed label directive. 2937 | 2938 | 6. If directive name is data-confidentiality 2939 | and conf is null, let conf be label 2940 | value. 2941 | 2942 | 7. Else, if directive name is 2943 | data-integrity and int is null, 2944 | let int be label value. 2945 | 2946 | 8. Else, terminate the rest of these sub-steps. 2947 | 2948 | The user agent SHOULD report a warning that the server provided a 2949 | malformed label directive. 2950 | 4. Return conf and int. 2951 | 2952 |
2953 | To make it easier for developers to debug applications, user 2954 | agents SHOULD report the directives that were ignored. 2955 |
2956 |
2957 | 2958 |
2959 |

2960 | Parse labeled context metadata 2961 |

2962 | 2963 | To parse labeled context metadata metadata for 2964 | origin self, the user agent MUST use an algorithm 2965 | equivalent to the following: 2966 | 2967 | 1. Let conf be null. 2968 | 2969 | 2. Let int be null. 2970 | 2971 | 3. Let priv be null. 2972 | 2973 | 4. For each non-empty token returned by 2974 | strictly splitting 2975 | the string metadata on the character U+003B SEMICOLON (;): 2976 | 2977 | 1. Skip whitespace. 2978 | 2979 | 2. Collect a sequence of characters that are 2980 | not space characters. The collected characters 2981 | are the directive name. 2982 | 2983 | 3. If there are characters remaining in token, 2984 | skip ahead exactly one character (which must be a 2985 | space character). 2986 | 2987 | 4. The remaining characters in token (if any) are 2988 | the directive value. 2989 | 2990 | 5. Let label value be the label returned by 2991 | invoking the parse label-expression algorithm with the 2992 | directive value and self. If parsing 2993 | failed, terminate the rest of these sub-steps. 2994 | 2995 | The user agent SHOULD report a warning that the server provided a 2996 | malformed label directive. 2997 | 2998 | 6. If directive name is 2999 | ctx-confidentiality and conf is 3000 | null, let conf be label value. 3001 | 3002 | 7. Else, if directive name is 3003 | ctx-integrity and int is null, 3004 | let int be label value. 3005 | 3006 | 8. Else, if directive name is 3007 | ctx-privilege and priv is null, 3008 | let priv be a newly created privilege whose 3009 | internal privilege label is set to label 3010 | value. 3011 | 3012 | 9. Else, terminate the rest of these sub-steps. 3013 | 3014 | The user agent SHOULD report a warning that the server provided a 3015 | malformed label directive. 3016 | 3017 | 5. Return conf, int, and priv. 3018 | 3019 |
3020 | To make it easier for developers to debug applications, user 3021 | agents SHOULD report the directives that were ignored. 3022 |
3023 |
3024 | 3025 |
3026 |

3027 | Parse label expression 3028 |

3029 | 3030 | The parse label-expression algorithm takes a 3031 | input representing a label-expression and a 3032 | stringified origin URL self and returns the parsed Label. If at any point the algorithm says that it "fails", 3034 | this means that it is aborted at that point and returns nothing. 3035 | The user agent MUST use an algorithm equivalent to the following: 3036 | 3037 | 1. Let label be a new empty label. 3038 | 3039 | 2. If input is not a string, fail. 3040 | 3041 | 3. Strip and collapse whitespace from input. 3042 | 3043 | 4. Let AND expr be the remaining characters in input. 3044 | 3045 | 5. If AND expr is the string `"'none'"`, return 3046 | label and terminate this algorithm. 3047 | 3048 | 6. Split the AND expr string on `"AND"`. Let AND 3049 | tokens be the resulting list of tokens, if the splitting didn't 3050 | fail. Otherwise, fail. 3051 | 3052 | 7. For each token in AND tokens, run the following sub-steps: 3053 | 3054 | 1. Let input be the token. 3055 | 3056 | 2. If the number of elements in AND tokens is greater than 1: 3057 | 3058 | 1. If first character of input is not `"("` (UA+0028), fail. 3059 | 3060 | 2. If last character of input is not `")"` (UA+0029), fail. 3061 | 3062 | 3. Remove the first and last characters from input. 3063 | 3064 | 3. Else, if first character of input is `"("` (UA+0028) or if 3065 | last character of input is `")"` (UA+0029): 3066 | 3067 | 1. If first character of input is not `"("` (UA+0028), fail. 3068 | 3069 | 2. If last character of input is not `")"` (UA+0029), fail. 3070 | 3071 | 3. Remove the first and last characters from input. 3072 | 3073 | 4. Let OR expr be the remaining characters in input. 3074 | 3075 | 5. Let orExp be a new empty label. 3076 | 3077 | 6. Split the string OR expr on `"OR"`. Let OR 3078 | tokens be the resulting list of tokens, if the splitting didn't 3079 | fail. Otherwise, fail. 3080 | 3081 | 7. For each token in OR tokens, run the following sub-steps: 3082 | 3083 | 1. Let prin be the token. 3084 | 3085 | 2. If prin is the string `"'self'"`, set prin be self. 3086 | 3087 | 3. Set orExp to orExp.or(prin), 3088 | if calling the or method didn't throw an exception. Otherwise, fail. 3089 | 3090 | Note, the method throws an exception if prin is not a 3091 | principal. Hence, this algorithm should fail. 3092 | 3093 | 8. Set label to label.and(orExp). 3094 | 3095 | 8. Return label. 3096 | 3097 | When a user agent has to split a string on particular 3098 | delimiter characters delimiter, it MUST use an algorithm 3099 | equivalent to the following: 3100 | 3101 | 1. Let input be the string being split. 3102 | 3103 | 2. Let position be a pointer into input, initially 3104 | pointing at the start of the string. 3105 | 3106 | 3. Let delimiter length be the number of characters in delimiter. 3107 | 3108 | 4. Let tokens be the resulting list of tokens. 3109 | 3110 | 5. Strip leading and trailing whitespace. 3111 | 3112 | 6. If input is the empty string, fail. 3113 | 3114 | 7. While position is not past the end of input: 3115 | 3116 | 1. Let s be the empty string. 3117 | 3118 | 2. Skip whitespace. 3119 | 3120 | 3. Let done be `false`. 3121 | 3122 | 4. While done is `false` and position is not past 3123 | the end of input: 3124 | 3125 | 1. Collect a sequence of characters that are not space 3126 | characters. Append the resulting sequence to s. 3127 | 3128 | 3. If position points past the end of input, 3129 | set done to `true` and end these sub-steps. 3130 | 3131 | 4. Collect a sequence of characters that are space 3132 | characters. Let ws be the resulting sequence. 3133 | 3134 | 5. If the next delimiter length characters are an 3135 | ASCII case-insensitive match for the string 3136 | delimiter, then set done to `true` and advance 3137 | position by delimiter length characters. 3138 | 3139 | 6. Else, append ws to s. 3140 | 3141 | 5. Append s to tokens. 3142 | 3143 | 8. Return tokens. 3144 | 3145 |
3146 | 3147 |
3148 | 3149 | 3158 |
3159 |

IANA Considerations

3160 | 3161 |

3162 | The Sec-COWL HTTP Header Field 3163 |

3164 | The permanent message header field registry should be updated with the 3165 | following registration [[!RFC3864]]: 3166 | 3167 |
3168 |
Header field name
3169 |
Sec-COWL
3170 | 3171 |
Applicable protocol
3172 |
http
3173 | 3174 |
Status
3175 |
standard
3176 | 3177 |
Author/Change controller
3178 |
W3C
3179 | 3180 |
Specification document
3181 |
This specification (See [[#header]])
3182 |
3183 | 3184 |

3185 | The application/labeled-json MIME media type 3186 |

3187 | 3188 |
3189 |
Type name
3190 |
application
3191 | 3192 |
Subtype name
3193 |
labeled-json
3194 | 3195 |
Required parameters
3196 |
Same as for application/json. [[JSON]]
3197 | 3198 |
Optional parameters
3199 |
Same as for application/json. [[JSON]]
3200 | 3201 |
Encoding considerations
3202 |
Same as for application/json. [[JSON]]
3203 | 3204 |
Security considerations
3205 |
Same as for application/json. [[JSON]]
3206 | 3207 |
Interoperability considerations
3208 |
Same as for application/json. [[JSON]]
3209 | 3210 |
Published specification
3211 |
3212 | Labeling a resource with the application/labeled-json 3213 | type asserts that the resource is a JSON text that consists of an 3214 | object with a single entry called "confidentiality" 3215 | consisting of a string, a single entry called "integrity" 3216 | consisting of string, and a single entry called "object" 3217 | consisting of a JSON object. The relevant specifications are the 3218 | JSON specification and this specification. [[JSON]] 3219 |
3220 | 3221 |
Author/Change controller
3222 |
W3C
3223 |
3224 |
3225 | 3226 | 3235 |
3236 |

Acknowledgements

3237 | 3238 | Thanks to Niklas Andreasson, Dan Boneh, Brendan Eich, Lon Ingram, Brad Hill, 3239 | Dave Herman, Bobby Holley, Brad Karp, Jonathan Kingston, Petr Marchenko, 3240 | David Mazières, Devon Rifkin, Alejandro Russo, Brian Smith, and the W3C 3241 | WebAppSec team for influencing (directly or otherwise) the design of COWL 3242 | and/or their comments on this document. 3243 |
3244 | -------------------------------------------------------------------------------- /published/default.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Style sheet for WebAppSec specifications (stolen wholesale from the CSSWG), 3 | * to be used in addition to http://www.w3.org/StyleSheets/TR/W3C-{WD,PR,REC} 4 | */ 5 | 6 | @media print { 7 | html { margin: 0 !important } 8 | body { font-family: serif } 9 | th, td { font-family: inherit } 10 | a { color: inherit !important } 11 | .example:before { font-family: serif !important } 12 | a:link, a:visited { text-decoration: none !important } 13 | a:link:after, a:visited:after { /* create a cross-ref "see..." */ } 14 | } 15 | @page { 16 | margin: 1.5cm 1.1cm; 17 | } 18 | 19 | body { 20 | counter-reset: exampleno figure issue; 21 | max-width: 50em; 22 | margin: 0 auto !important; 23 | } 24 | 25 | /* Pagination */ 26 | h1, h2, h3, h4, h5, h6 { page-break-after: avoid } 27 | figure, div.figure, div.sidefigure, pre, table.propdef, table.propdef-extra, 28 | .example { page-break-inside: avoid } 29 | dt { page-break-after: avoid } 30 | 31 | span.id {float: right; font-weight: bold} 32 | 33 | /* General Structural Markup */ 34 | h2, h3, h5, h6 { margin-top: 3em; } 35 | 36 | /* #subtitle is a subtitle in an H2 under the H1 */ 37 | h1 + h2, #subtitle + h2 { margin-top: 0; } 38 | 39 | h4 { margin-top: 4em; } 40 | 41 | h2 + h3, h3 + h4, h4 + h5, h5 + h6 { margin-top: 1.2em } 42 | 43 | hr:not([title]) { 44 | font-size: 1.5em; 45 | text-align: center; 46 | margin: 1em auto; 47 | border: transparent solid; 48 | background: transparent; 49 | } 50 | hr:not([title])::before { 51 | content: "\1F411\2003\2003\1F411\2003\2003\1F411"; 52 | } 53 | 54 | p, div.note, div.issue, details.why { 55 | margin-top: 1em; 56 | margin-bottom: 1em; 57 | } 58 | 59 | dd > p:first-child, li > p:first-child, .note > p:first-child, .issue > p:first-child { 60 | margin-top: 0 61 | } 62 | 63 | pre { 64 | margin-top: 1em; 65 | margin-bottom: 1em; 66 | } 67 | 68 | pre, code { 69 | font-family: Menlo, Consolas, "DejaVu Sans Mono", monospace; 70 | font-size: .9em; 71 | } 72 | 73 | img { 74 | border-style: none; 75 | color: white; 76 | } 77 | .toc { 78 | } 79 | 80 | body { 81 | line-height: 1.5; 82 | } 83 | 84 | a.logo:link, a.logo:visited { 85 | padding: 0; 86 | border-style: none; 87 | } 88 | 89 | dl dd { margin: 0 0 1em 2em } 90 | .head dd { margin-bottom: 0; } 91 | ul, ol { margin-left: 0; padding-left: 2em; } 92 | li { margin: 0.25em 2em 0.5em 0; padding-left: 0 } 93 | 94 | ul.indexlist { margin-left: 0; columns: 13em; } 95 | ul.indexlist li { margin-left: 0; list-style: none } 96 | ul.indexlist li li { margin-left: 1em } 97 | ul.indexlist a { font-weight: bold; } 98 | ul.indexlist ul, ul.indexlist dl { font-size: smaller; } 99 | ul.indexlist dl { margin-top: 0; } 100 | ul.indexlist dt { margin: .2em 0 .2em 20px;} 101 | ul.indexlist dd { margin: .2em 0 .2em 40px;} 102 | 103 | /* .self-link is a link to the element */ 104 | .heading, .issue, .note, .example, li, dt { position: relative; } 105 | a.self-link { 106 | position: absolute; 107 | top: 0; 108 | left: -2.5em; 109 | width: 2em; 110 | height: 2em; 111 | text-align: center; 112 | border: none; 113 | transition: opacity .2s; 114 | opacity: .5; 115 | } 116 | a.self-link:hover { 117 | opacity: 1; 118 | } 119 | .heading > a.self-link { 120 | font-size: 83%; 121 | } 122 | li > a.self-link { 123 | left: -3.5em; 124 | } 125 | dfn > a.self-link { 126 | top: auto; 127 | left: auto; 128 | opacity: 0; 129 | width: 1.5em; 130 | height: 1.5em; 131 | background: gray; 132 | color: white; 133 | font-style: normal; 134 | transition: opacity .2s, background-color .2s, color .2s; 135 | } 136 | dfn:hover > a.self-link { 137 | opacity: 1; 138 | } 139 | dfn > a.self-link:hover { 140 | color: black; 141 | } 142 | 143 | a.self-link::before { content: "¶"; } 144 | .heading > a.self-link::before { content: "§"; } 145 | dfn > a.self-link::before { content: "#"; } 146 | 147 | /* Examples */ 148 | 149 | .example { 150 | counter-increment: exampleno; 151 | } 152 | .example:before { 153 | content: "Example"; 154 | content: "Example " counter(exampleno); 155 | min-width: 7.5em; 156 | text-transform: uppercase; 157 | display: block; 158 | } 159 | div.illegal-example:before, pre.illegal-example:before { 160 | content: "Invalid Example"; 161 | content: "Invalid Example" counter(exampleno); 162 | } 163 | .example, .illegal-example, div.html, div.illegal-html, div.xml, 164 | div.illegal-xml, pre.html, 165 | pre.illegal-html, pre.xml, pre.illegal-xml { 166 | padding: 0.5em; 167 | margin: 1em 0; 168 | position: relative; 169 | clear: both; 170 | } 171 | pre.example, pre.illegal-example, pre.html, 172 | pre.illegal-html, pre.xml, pre.illegal-xml { 173 | padding-top: 1.5em; 174 | } 175 | pre.illegal-example { color: red } 176 | div.illegal-example { color: red } 177 | div.illegal-example p { color: black } 178 | 179 | div.html { color: #600 } 180 | pre.html { color: #600 } 181 | pre.illegal-html { color: red } 182 | div.illegal-html { color: red } 183 | div.illegal-html p { color: black } 184 | pre.deprecated-html { color: red } 185 | div.deprecated-html { color: red } 186 | div.deprecated-html p { color: black } 187 | 188 | div.xml { color: #600 } 189 | pre.xml { color: #600 } 190 | pre.illegal-xml { color: red } 191 | div.illegal-xml { color: red } 192 | div.illegal-xml p { color: black } 193 | 194 | .css, .property { color: #005a9c } /* inline CSS code (SPAN/CODE) */ 195 | code.css { font-family: inherit; font-size: 100% } 196 | code.html { color: #600 } /* inline HTML */ 197 | code.xml { color: #600 } /* inline XML */ 198 | .property { font: inherit; white-space: nowrap; } /* name of a CSS property (SPAN) */ 199 | .descriptor { } /* name of a CSS descriptor (SPAN) */ 200 | .type { font-style: italic } /* A value for a property */ 201 | 202 | /* Autolinks produced using Bikeshed. */ 203 | [data-link-type="property"]::before, 204 | [data-link-type="propdesc"]::before, 205 | [data-link-type="descriptor"]::before, 206 | [data-link-type="value"]::before, 207 | [data-link-type="function"]::before, 208 | [data-link-type="at-rule"]::before, 209 | [data-link-type="selector"]::before, 210 | [data-link-type="maybe"]::before {content: "\2018";} 211 | [data-link-type="property"]::after, 212 | [data-link-type="propdesc"]::after, 213 | [data-link-type="descriptor"]::after, 214 | [data-link-type="value"]::after, 215 | [data-link-type="function"]::after, 216 | [data-link-type="at-rule"]::after, 217 | [data-link-type="selector"]::after, 218 | [data-link-type="maybe"]::after {content: "\2019";} 219 | [data-link-type].production::before, 220 | [data-link-type].production::after, 221 | .prod [data-link-type]::before, 222 | .prod [data-link-type]::after { content: ""; } 223 | 224 | 225 | /* Element-type link styling */ 226 | [data-link-type=element] { font-family: monospace; } 227 | [data-link-type=element]::before { content: "<" } 228 | [data-link-type=element]::after { content: ">" } 229 | 230 | dfn { font-weight: bolder; } 231 | 232 | .issue, .note, .example, .why { 233 | padding: .5em; 234 | /* padding: .5rem; /* proposed unit in css3-values */ 235 | border-left-width: .5em; 236 | /* border-left-width: .5rem; /* proposed unit in css3-values */ 237 | border-left-style: solid; 238 | } 239 | span.note, span.issue { 240 | padding: .1em .5em .15em; 241 | border-right-width: .5em; 242 | border-right-style: solid; 243 | } 244 | 245 | /* Open issue / editorial remark; not intended for a final publication */ 246 | .issue { 247 | border-color: #E05252; 248 | background: #FBE9E9; 249 | counter-increment: issue; 250 | overflow: auto; 251 | } 252 | 253 | .issue:before { 254 | content: "Issue " counter(issue); 255 | padding-right: 1em; 256 | text-transform: uppercase; 257 | color: #E05252; 258 | } 259 | 260 | /* Class note is a non-normative note. May be inline or a P or DIV */ 261 | .note, .why { 262 | border-color: #52E052; 263 | background: #E9FBE9; 264 | overflow: auto; 265 | } 266 | 267 | .normref { color: red } 268 | .informref { color: green } 269 | 270 | /* Example box */ 271 | .example { 272 | border-color: #E0CB52; 273 | background: #FCFAEE; 274 | overflow: auto; 275 | } 276 | 277 | .example:before { 278 | color: #B9AB2D; 279 | font-family: sans-serif; 280 | } 281 | 282 | details.why { 283 | border-color: #52E052; 284 | background: #E9FBE9; 285 | display: block; 286 | } 287 | 288 | details.why > summary { 289 | font-style: italic; 290 | display: block; 291 | } 292 | 293 | details.why[open] > summary { 294 | border-bottom: 1px silver solid; 295 | } 296 | 297 | /* ToC not indented, but font style shows hierarchy */ 298 | ul.toc {margin: 1em 0; padding: 0; line-height: 1.3; font-weight: bold; /*text-transform: uppercase;*/ } 299 | ul.toc ul {margin: 0; padding: 0; font-weight: normal; text-transform: none; } 300 | ul.toc ul ul {margin: 0 0 0 2em; font-style: italic; } 301 | ul.toc ul ul ul {margin: 0} 302 | ul.toc > li {margin: 1.5em 0; padding: 0; } 303 | ul.toc ul.toc li { margin: 0.3em 0 0 0; } 304 | ul.toc a { text-decoration: none; border-bottom-style: none; } 305 | ul.toc a:hover, ul.toc a:focus { border-bottom-style: solid; } 306 | /* 307 | ul.toc li li li, ul.toc li li li ul {margin-left: 0; display: inline} 308 | ul.toc li li li ul, ul.toc li li li ul li {margin-left: 0; display: inline} 309 | */ 310 | 311 | /* Section numbers in a column of their own */ 312 | ul.toc span.secno {float: left; width: 4em; margin-left: -5em} 313 | ul.toc ul ul span.secno { margin-left: -7em; } 314 | /*ul.toc span.secno {text-align: right}*/ 315 | ul.toc li {clear: both} 316 | ul.toc {margin-left: 5em} 317 | /* If we had 'tab', floats would not be needed here: 318 | ul.toc span.secno {tab: 5em right; margin-right: 1em} 319 | ul.toc li {text-indent: 5em hanging} 320 | The second line in case items wrap 321 | */ 322 | 323 | ul.index { 324 | list-style: none; 325 | } 326 | 327 | s, del {text-decoration: line-through; color: red} 328 | u, ins {text-decoration: underline; color: #080} 329 | 330 | div.figure, p.figure, div.sidefigure, figure { 331 | text-align: center; 332 | margin: 2.5em 0; 333 | } 334 | div.figure pre, div.sidefigure pre, figure pre { 335 | text-align: left; 336 | display: table; 337 | margin: 1em auto; 338 | } 339 | .figure table, figure table { 340 | margin: auto; 341 | } 342 | div.sidefigure, figure.sidefigure { 343 | float: right; 344 | width: 50%; 345 | margin: 0 0 0.5em 0.5em 346 | } 347 | div.figure img, div.sidefigure img, figure img, 348 | div.figure object, div.sidefigure object, figure object { 349 | display: block; 350 | margin: auto; 351 | max-width: 100% 352 | } 353 | p.caption, figcaption, caption { 354 | text-align: center; 355 | font-style: italic; 356 | font-size: 90%; 357 | } 358 | p.caption:before, figcaption:before { 359 | content: "Figure " counter(figure) ". "; 360 | font-weight: bold; 361 | } 362 | p.caption, figcaption { 363 | counter-increment: figure; 364 | } 365 | 366 | /* DL list is indented, but figure inside it is not */ 367 | dd { margin-left: 2em } 368 | dd div.figure, dd figure { margin-left: -2em } 369 | 370 | sup { 371 | vertical-align: super; 372 | font-size: 80% 373 | } 374 | 375 | /* "Equations" (not real MathML, but simple HTML) are put in a 376 | blockquote and may have an equation number. We make sure the 377 | blockquote has enough margin on the right and then put the equation 378 | number there. */ 379 | 380 | blockquote { 381 | margin: 0.5em 4em 0.5em 2em; 382 | text-indent: 0; 383 | } 384 | .eqno { 385 | text-align: right; 386 | float: right; 387 | width: 3em; 388 | margin: 0 -4em 0 0; 389 | font-weight: bold; 390 | /* background: silver; color: black; padding: 0.1em */ 391 | } 392 | 393 | table.equiv-table { border-spacing: 0; margin: 0.5em 0 } 394 | table.equiv-table th, table.equiv-table td { padding: 0.3em } 395 | table.equiv-table th { text-align: left } 396 | /* table.equiv-table th:first-child { text-align: right } */ 397 | table.equiv-table td, table.equiv-table th { border-bottom: thin solid #666 } 398 | table.equiv-table { border-bottom: hidden } 399 | table.equiv-table { empty-cells: show } 400 | table.equiv-table caption { margin: 0.5em 0 0 0 } 401 | 402 | /* Style for table of properties */ 403 | table.proptable { 404 | font-size: small; 405 | border-collapse: collapse; 406 | border-spacing: 0; 407 | text-align: left; 408 | margin: 1em 0; 409 | } 410 | 411 | table.proptable td, table.proptable th { 412 | padding: 0.4em; 413 | text-align: center; 414 | } 415 | 416 | table.proptable tr:hover td { 417 | background: #DEF; 418 | } 419 | 420 | 421 | /* Style for table that defines a property or a descriptor */ 422 | table.propdef, table.propdef-extra, table.descdef, table.definition-table { 423 | border-spacing: 0; 424 | padding: 0 1em 0.5em; 425 | width: 100%; 426 | table-layout: fixed; 427 | background: #DEF; 428 | margin: 1.2em 0; 429 | border-left: 0.5em solid #8CCBF2; 430 | } 431 | 432 | table.propdef td, table.propdef-extra td, table.descdef td, table.definition-table td, 433 | table.propdef th, table.propdef-extra th, table.descdef th, table.definition-table th { 434 | padding: 0.5em; 435 | vertical-align: baseline; 436 | border-bottom: 1px solid #bbd7e9; 437 | } 438 | /* 439 | table.propdef dfn, table.propdef-extra dfn, table.descdef dfn { 440 | font-weight: bold; 441 | font-style: normal 442 | } 443 | */ 444 | 445 | table.propdef td:first-child, 446 | table.propdef-extra td:first-child, 447 | table.descdef td:first-child, 448 | table.definition-table td:first-child, 449 | table.propdef th:first-child, 450 | table.propdef-extra th:first-child, 451 | table.descdef th:first-child, 452 | table.definition-table th:first-child { 453 | font-style: italic; 454 | font-weight: normal; 455 | width: 8.3em; 456 | padding-left: 1em; 457 | } 458 | table.propdef td[colspan]:first-child, 459 | table.propdef-extra td[colspan]:first-child, 460 | table.descdef td[colspan]:first-child, 461 | table.definition-table td[colspan]:first-child, 462 | table.propdef th[colspan]:first-child, 463 | table.propdef-extra th[colspan]:first-child, 464 | table.descdef th[colspan]:first-child, 465 | table.definition-table th[colspan]:first-child { 466 | font-style: inherit 467 | } 468 | table.propdef tr:first-child, 469 | table.propdef-extra tr:first-child, 470 | table.descdef tr:first-child, 471 | table.definition-table tr:first-child { 472 | 473 | } 474 | 475 | table.propdef > tbody > tr:last-child th, 476 | table.propdef-extra > tbody > tr:last-child th, 477 | table.descdef > tbody > tr:last-child th, 478 | table.definition-table > tbody > tr:last-child th, 479 | table.propdef > tbody > tr:last-child td, 480 | table.propdef-extra > tbody > tr:last-child td, 481 | table.descdef > tbody > tr:last-child td, 482 | table.definition-table > tbody > tr:last-child td { 483 | border-bottom: 0; 484 | } 485 | 486 | table.propdef tr:first-child th, 487 | table.propdef-extra tr:first-child th, 488 | table.descdef tr:first-child th, 489 | table.definition-table tr:first-child th, 490 | table.propdef tr:first-child td, 491 | table.propdef-extra tr:first-child td, 492 | table.descdef tr:first-child td, 493 | table.definition-table tr:first-child td { 494 | padding-top: 1em; 495 | } 496 | 497 | /* For when values are extra-complex and need formatting for readability */ 498 | table td.pre { 499 | white-space: pre-wrap; 500 | } 501 | 502 | /* A footnote at the bottom of a propdef */ 503 | table.propdef td.footnote, 504 | table.propdef-extra td.footnote, 505 | table.descdef td.footnote, 506 | table.definition-table td.footnote { 507 | padding-top: 0.6em; 508 | width: auto 509 | } 510 | table.propdef td.footnote:before, 511 | table.propdef-extra td.footnote:before, 512 | table.descdef td.footnote:before, 513 | table.definition-table td.footnote:before { 514 | content: " "; 515 | display: block; 516 | height: 0.6em; 517 | width: 4em; 518 | border-top: thin solid; 519 | } 520 | 521 | /* The link in the first column in the property table (formerly a TD) */ 522 | table.proptable td .property, 523 | table.proptable th .property { 524 | display: block; 525 | text-align: left; 526 | font-weight: bold; 527 | } 528 | 529 | 530 | /* Styling for IDL fragments */ 531 | 532 | pre.idl { 533 | padding: .5em 1em; 534 | background: #DEF; 535 | margin: 1.2em 0; 536 | border-left: 0.5em solid #8CCBF2; 537 | } 538 | pre.idl :link, pre.idl :visited { 539 | color:inherit; 540 | background:transparent; 541 | } 542 | 543 | 544 | /* CSS modules typically don't use MUST, SHOULD etc. from RFC 2119, 545 | or, if they do, they don't put them in uppercase. But the following 546 | class is provided in case a spec wants to use RFC 2119 terms in 547 | uppercase in the source. */ 548 | 549 | em.rfc2119 { 550 | text-transform: lowercase; 551 | font-variant: small-caps; 552 | font-style: normal 553 | } 554 | 555 | /* In Profile specs, a table of required features: */ 556 | 557 | table.features th { 558 | background: #00589f; 559 | color: #fff; 560 | text-align: left; 561 | padding: 0.2em 0.2em 0.2em 0.5em; 562 | } 563 | table.features td { 564 | vertical-align: top; 565 | border-bottom: 1px solid #ccc; 566 | padding: 0.3em 0.3em 0.3em 0.7em; 567 | } 568 | 569 | 570 | /* Style for data tables (and properly marked-up proptables) */ 571 | 572 | .data, .proptable { 573 | margin: 1em auto; 574 | border-collapse: collapse; 575 | width: 100%; 576 | border: hidden; 577 | } 578 | .data { 579 | text-align: center; 580 | width: auto; 581 | } 582 | .data caption { 583 | width: 100%; 584 | } 585 | 586 | .data td, .data th, 587 | .proptable td, .proptable th { 588 | padding: 0.5em; 589 | border-width: 1px; 590 | border-color: silver; 591 | border-top-style: solid; 592 | } 593 | 594 | .data thead td:empty { 595 | padding: 0; 596 | border: 0; 597 | } 598 | 599 | .data thead th[scope="row"], 600 | .proptable thead th[scope="row"] { 601 | text-align: right; 602 | color: inherit; 603 | } 604 | 605 | .data thead, 606 | .proptable thead, 607 | .data tbody, 608 | .proptable tbody { 609 | color: inherit; 610 | border-bottom: 2px solid; 611 | } 612 | 613 | .data colgroup { 614 | border-left: 2px solid; 615 | } 616 | 617 | .data tbody th:first-child, 618 | .proptable tbody th:first-child , 619 | .data tbody td[scope="row"]:first-child, 620 | .proptable tbody td[scope="row"]:first-child { 621 | text-align: right; 622 | color: inherit; 623 | border-right: 2px solid; 624 | border-top: 1px solid silver; 625 | padding-right: 1em; 626 | } 627 | .data.define td:last-child { 628 | text-align: left; 629 | } 630 | 631 | .data tbody th[rowspan], 632 | .proptable tbody th[rowspan], 633 | .data tbody td[rowspan], 634 | .proptable tbody td[rowspan]{ 635 | border-left: 1px solid silver; 636 | } 637 | 638 | .data tbody th[rowspan]:first-child, 639 | .proptable tbody th[rowspan]:first-child, 640 | .data tbody td[rowspan]:first-child, 641 | .proptable tbody td[rowspan]:first-child{ 642 | border-left: 0; 643 | border-right: 1px solid silver; 644 | } 645 | 646 | .complex.data th, 647 | .complex.data td { 648 | border: 1px solid silver; 649 | } 650 | 651 | .data td.long { 652 | vertical-align: baseline; 653 | text-align: left; 654 | } 655 | 656 | .data img { 657 | vertical-align: middle; 658 | } 659 | 660 | table.propdef { 661 | table-layout: auto; 662 | } 663 | .propdef th { 664 | font-style: italic; 665 | font-weight: normal; 666 | text-align: left; 667 | width: 3em; 668 | } 669 | dt dfn code { 670 | font-size: inherit; 671 | } 672 | 673 | /* Style for switch/case
s */ 674 | dl.switch { 675 | padding-left: 2em; 676 | } 677 | dl.switch > dt { 678 | text-indent: -1.5em; 679 | } 680 | dl.switch > dt:before { 681 | content: '\21AA'; 682 | padding: 0 0.5em 0 0; 683 | display: inline-block; 684 | width: 1em; 685 | text-align: right; 686 | line-height: 0.5em; 687 | } 688 | 689 | 690 | /* Style for At Risk features (intended as editorial aid, not intended for publishing) */ 691 | .atrisk::before { 692 | position: absolute; 693 | margin-left: -5em; 694 | margin-top: -2px; 695 | padding: 4px; 696 | border: 1px solid; 697 | content: 'At risk'; 698 | font-size: small; 699 | background-color: white; 700 | color: gray; 701 | border-radius: 1em; 702 | text-align: center; 703 | } 704 | 705 | .toc .atrisk::before { content:none } 706 | 707 | 708 | /* This is mostly to make the list inside the CR exit criteria more compact. */ 709 | ol.inline, ol.inline li {display: inline; padding: 0; margin: 0} 710 | ol.inline {counter-reset: list-item} 711 | ol.inline li {counter-increment: list-item} 712 | ol.inline li:before {content: "(" counter(list-item) ") "; font-weight: bold} 713 | 714 | /* This styles the obsoletion notice on some of our older/abandoned specs. */ 715 | details.annoying-warning[open] { 716 | background: #fdd; 717 | color: red; 718 | font-weight: bold; 719 | text-align: center; 720 | padding: .5em; 721 | border: thick solid red; 722 | border-radius: 1em; 723 | position: fixed; 724 | left: 1em; 725 | right: 1em; 726 | bottom: 1em; 727 | z-index: 1000; 728 | } 729 | 730 | details.annoying-warning:not([open]) > summary { 731 | background: #fdd; 732 | color: red; 733 | font-weight: bold; 734 | text-align: center; 735 | padding: .5em; 736 | } 737 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [ 3 | "49309" 4 | ], 5 | "contacts": [ 6 | "wseltzer" 7 | ], 8 | "shortName": "COWL", 9 | "repo-type": "rec-track" 10 | } --------------------------------------------------------------------------------