Attachment #382990: CSP Work in Progress (v2) for bug #493857

View | Details | Raw Unified | Return to bug 493857
Collapse All | Expand All

(-)b9ad86e82328 (+86 lines)
Added Link Here 
Added Link Here 
1
/* ***** BEGIN LICENSE BLOCK *****
2
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3
 *
4
 * The contents of this file are subject to the Mozilla Public License Version
5
 * 1.1 (the "License"); you may not use this file except in compliance with
6
 * the License. You may obtain a copy of the License at
7
 * http://www.mozilla.org/MPL/
8
 *
9
 * Software distributed under the License is distributed on an "AS IS" basis,
10
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
 * for the specific language governing rights and limitations under the
12
 * License.
13
 *
14
 * The Original Code is the nsCSPContentPolicy service.
15
 *
16
 * The Initial Developer of the Original Code is
17
 *   Sid Stamm <sid@mozilla.com>
18
 *
19
 * Contributor(s):
20
 *   Brandon Sterne <bsterne@mozilla.com>
21
 *
22
 * Alternatively, the contents of this file may be used under the terms of
23
 * either the GNU General Public License Version 2 or later (the "GPL"), or
24
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25
 * in which case the provisions of the GPL or the LGPL are applicable instead
26
 * of those above. If you wish to allow use of your version of this file only
27
 * under the terms of either the GPL or the LGPL, and not to allow others to
28
 * use your version of this file under the terms of the MPL, indicate your
29
 * decision by deleting the provisions above and replace them with the notice
30
 * and other provisions required by the GPL or the LGPL. If you do not delete
31
 * the provisions above, a recipient may use your version of this file under
32
 * the terms of any one of the MPL, the GPL or the LGPL.
33
 *
34
 * ***** END LICENSE BLOCK ***** */
35
36
#include "nsISupports.idl"
37
#include "nsIURI.idl"
38
39
/**
40
 * IContentSecurityPolicy  
41
 * Describes an XPCOM component used to model an enforce CSPs.
42
 */
43
44
[scriptable, uuid(AB36A2BF-CB32-4AA6-AB41-6B4E4444A221)]
45
interface IContentSecurityPolicy : nsISupports
46
{
47
48
  /**
49
   * Set to true when the CSP has been read in and parsed and is ready to
50
   * enforce.  This is a barrier for the nsDocument so it doesn't load any
51
   * sub-content until either it knows that a CSP is ready or will not be used.
52
   */
53
  attribute boolean isInitialized;
54
55
  /**
56
   * A read-only string version of the policy for debugging.
57
   */
58
  readonly attribute AString policy;
59
60
  void refinePolicy(in AString policyString);
61
62
  /**
63
   * Delegate method called by the service when sub-elements of the protected
64
   * document are being loaded.  Given a bit of information about the request,
65
   * decides whether or not the policy is satisfied.
66
   */
67
  short shouldLoad(in unsigned long   aContentType, 
68
                   in nsIURI          aContentLocation, 
69
                   in nsIURI          aRequestOrigin, 
70
                   in nsISupports     aContext, 
71
                   in ACString        aMimeTypeGuess, 
72
                   in nsISupports     aExtra);
73
74
  /**
75
   * Delegate method called by the service when sub-elements of the protected
76
   * document are being processed.  Given a bit of information about the request,
77
   * decides whether or not the policy is satisfied.
78
   */
79
  short shouldProcess(in unsigned long   aContentType, 
80
                      in nsIURI          aContentLocation, 
81
                      in nsIURI          aRequestOrigin, 
82
                      in nsISupports     aContext, 
83
                      in ACString        aMimeType,
84
                      in nsISupports     aExtra);
85
86
};
(-)a/caps/idl/Makefile.in (+1 lines)
Line     Link Here 
 Lines 45-56   include $(DEPTH)/config/autoconf.mk Link Here 
45
MODULE		= caps
45
MODULE		= caps
46
GRE_MODULE	= 1
46
GRE_MODULE	= 1
47
47
48
XPIDLSRCS	= \
48
XPIDLSRCS	= \
49
		nsIScriptSecurityManager.idl \
49
		nsIScriptSecurityManager.idl \
50
		nsIPrincipal.idl \
50
		nsIPrincipal.idl \
51
		nsISignatureVerifier.idl \
51
		nsISignatureVerifier.idl \
52
		nsISecurityCheckedComponent.idl \
52
		nsISecurityCheckedComponent.idl \
53
		IContentSecurityPolicy.idl \
53
		$(NULL)
54
		$(NULL)
54
55
55
include $(topsrcdir)/config/rules.mk
56
include $(topsrcdir)/config/rules.mk
56
57
(-)b9ad86e82328 (+826 lines)
Added Link Here 
Added Link Here 
1
/* ***** BEGIN LICENSE BLOCK *****
2
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3
 *
4
 * The contents of this file are subject to the Mozilla Public License Version
5
 * 1.1 (the "License"); you may not use this file except in compliance with
6
 * the License. You may obtain a copy of the License at
7
 * http://www.mozilla.org/MPL/
8
 *
9
 * Software distributed under the License is distributed on an "AS IS" basis,
10
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
 * for the specific language governing rights and limitations under the
12
 * License.
13
 *
14
 * The Original Code is the Content Security Policy data structures.
15
 *
16
 * The Initial Developer of the Original Code is
17
 * Sid Stamm <sid@mozilla.com>
18
 *
19
 * Contributor(s):
20
 *
21
 * Alternatively, the contents of this file may be used under the terms of
22
 * either the GNU General Public License Version 2 or later (the "GPL"), or
23
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
24
 * in which case the provisions of the GPL or the LGPL are applicable instead
25
 * of those above. If you wish to allow use of your version of this file only
26
 * under the terms of either the GPL or the LGPL, and not to allow others to
27
 * use your version of this file under the terms of the MPL, indicate your
28
 * decision by deleting the provisions above and replace them with the notice
29
 * and other provisions required by the GPL or the LGPL. If you do not delete
30
 * the provisions above, a recipient may use your version of this file under
31
 * the terms of any one of the MPL, the GPL or the LGPL.
32
 *
33
 * ***** END LICENSE BLOCK ***** */
34
35
36
/**
37
 * Content Security Policy Utilities
38
 * 
39
 * Overview
40
 * This contains a set of classes and utilities for CSP.  It is in this
41
 * separate file for testing purposes.
42
 */
43
44
// Module stuff
45
var EXPORTED_SYMBOLS = ["CSPRep", "CSPSourceList", "CSPSource", 
46
                        "CSPHost", "CSPWarning", "CSPError"];
47
48
function CSPWarning(aMsg) {
49
  //customize this to redirect output.
50
  aMsg = 'CSP WARN:  ' + aMsg;
51
  dump(aMsg);
52
  Components.classes["@mozilla.org/consoleservice;1"]
53
                    .getService(Components.interfaces.nsIConsoleService)
54
                    .logStringMessage(aMsg);
55
}
56
function CSPError(aMsg) {
57
  aMsg = 'CSP ERROR: ' + aMsg;
58
  dump(aMsg);
59
  Components.classes["@mozilla.org/consoleservice;1"]
60
                    .getService(Components.interfaces.nsIConsoleService)
61
                    .logStringMessage(aMsg);
62
}
63
function debug(aMsg) {
64
  aMsg = 'CSP debug: ' + aMsg;
65
  dump(aMsg);
66
  Components.classes["@mozilla.org/consoleservice;1"]
67
                    .getService(Components.interfaces.nsIConsoleService)
68
                    .logStringMessage(aMsg);
69
}
70
71
72
//:::::::::::::::::::::::: CLASSES ::::::::::::::::::::::::::// 
73
74
/**
75
 * Class that represents a parsed policy structure.
76
 */
77
function CSPRep() {
78
  // this gets set to true when the policy is done parsing, or when a
79
  // URI-borne policy has finished loading.
80
  this._isInitialized = false;
81
82
  // don't auto-populate _directives, so it is easier to find bugs
83
  this._directives = {};
84
85
}
86
CSPRep.SRC_DIRECTIVES = {
87
  ALLOW:            "allow",
88
  SCRIPT_SRC:       "script-src",
89
  STYLE_SRC:        "style-src",
90
  MEDIA_SRC:        "media-src",
91
  IMG_SRC:          "img-src",
92
  OBJECT_SRC:       "object-src",
93
  FRAME_SRC:        "frame-src",
94
  FRAME_ANCESTORS:  "frame-ancestors"
95
};
96
CSPRep.URI_DIRECTIVES = {
97
  REPORT_URI:       "report-uri",
98
  POLICY_URI:       "policy-uri"
99
};
100
101
102
/**
103
  * Factory to create a new CSPRep, parsed from a string.
104
  *
105
  * @param aStr
106
  *        string rep of a CSP
107
  * @param self (optional)
108
  *        string or CSPSource representing the "self" source
109
  * @returns
110
  *        an instance of CSPRep
111
  */
112
CSPRep.fromString = function(aStr, self) {
113
  var SD = CSPRep.SRC_DIRECTIVES;
114
  var UD = CSPRep.URI_DIRECTIVES;
115
  var pieces = aStr.split(/;\s*/);
116
  var aCSPR = new CSPRep();
117
  piece:for(var i in pieces) {
118
    //split off first token and figure out what to do
119
    var dirname = pieces[i].match(/(\S+)/)[0];
120
121
    //careful to trim the stuff so there's no extra tokens 
122
    var dirvalue = pieces[i].substring(dirname.length)
123
                            .replace(/^\s+/,'')
124
                            .replace(/\s+$/,'');
125
126
    //is it a SRC directive?
127
    for(var sdi in SD) {
128
      if(dirname == SD[sdi]) {
129
        var dv = CSPSourceList.fromString(dirvalue, self);
130
        //TODO: more robust check for errors in making DV
131
        aCSPR._directives[SD[sdi]] = dv;
132
        continue piece;
133
      }
134
    }
135
136
    //not a SRC directive, check URI directives
137
    if(dirname == UD.POLICY_URI) {
138
      // POLICY_URI can only be alone
139
      if(aCSPR._directives.length > 0 || pieces.length > 1) {
140
        CSPError("policy-uri directive can only appear alone");
141
        return CSPRep.fromString("allow none");
142
      }
143
144
      //TODO: verify that policy URI is same-origin
145
      var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]  
146
                  .createInstance(Ci.nsIXMLHttpRequest);  
147
148
      req.onerror = CSPError;
149
150
      // synchronous -- otherwise we need to architect a callback into the
151
      // xpcom component so that whomever creates the policy object gets
152
      // notified when it's loaded and ready to go.
153
      req.open("GET", dirvalue, false);
154
      //debug("Fetching URI " + dirvalue);
155
      req.send(null);  
156
      if(req.status == 200) {
157
        //debug("XHR returned: " + req.responseText);
158
        aCSPR = CSPRep.fromString(req.responseText, self);
159
        //remember where we got the policy
160
        aCSPR._directives[UD.POLICY_URI] = dirvalue;
161
        return aCSPR;
162
      }
163
      CSPError("Error fetching policy URI: server response was " + req.status);
164
      return CSPRep.fromString("allow none");
165
    }
166
    if(dirname == UD.REPORT_URI) {
167
      //TODO: do we want to support multiple URIs?
168
      //TODO: verify that report URI(s) is(are) same-origin
169
      aCSPR._directives[UD.REPORT_URI] = dirvalue;
170
      continue piece;
171
    }
172
173
    //no directives matched
174
    CSPWarning("Couldn't process unknown directive '" + dirname + "'");
175
  }
176
177
  if(aCSPR._directives.length < 1) {
178
    CSPError("invalid policy -- no recognized directives");
179
    return CSPRep.fromString("allow none");
180
  }
181
182
  if(!aCSPR._directives[SD.ALLOW]) {
183
    CSPWarning("allow directive is not present, using 'allow none'");
184
    aCSPR._directives[SD.ALLOW] = CSPSourceList.fromString("none");
185
  }
186
187
  aCSPR.makeExplicit();
188
  return aCSPR;
189
};
190
191
CSPRep.prototype = {
192
  /**
193
   * Returns array of all report uris defined, or none if there are none.
194
   */
195
  getReportURIs:
196
  function() {
197
    if(!this._directives[CSPRep.URI_DIRECTIVES.REPORT_URI])
198
      return [];
199
200
    var uris = this._directives[CSPRep.URI_DIRECTIVES.REPORT_URI].split(",");
201
    for(let i in uris) {
202
      //trim off surrounding whitespace
203
      uris[i] = uris[i].replace(/^\s+/, "").replace(/\s+$/, "");
204
    }
205
    return uris;
206
  },
207
208
  /**
209
   * Compares this CSPRep instance to another.
210
   */
211
  equals:
212
  function(that) {
213
    if(this._directives.length != that._directives.length) {
214
      return false;
215
    }
216
    for(var i in this._directives) {
217
      if(!that._directives[i] || !this._directives[i].equals(that._directives[i])) {
218
        return false;
219
      }
220
    }
221
    return true;
222
  },
223
224
  /**
225
   * Generates string representation of the policy.  Should be fairly similar
226
   * to the original.
227
   */
228
  toString:
229
  function csp_toString() {
230
    var dirs = [];
231
    for(var i in this._directives) {
232
      if(this._directives[i]) {
233
        dirs.push(i + " " + this._directives[i].toString());
234
      }
235
    }
236
    return dirs.join("; ");
237
  },
238
239
240
  /**
241
   * Determines if this directive accepts a URI.
242
   * @param aContext
243
   *        one of the SRC_DIRECTIVES defined above
244
   * @returns 
245
   *        true if the policy permits the URI in given context.
246
   */
247
  permits:
248
  function csp_permits(aURI, aContext) {
249
    //make sure the context is valid
250
    for(var i in CSPRep.SRC_DIRECTIVES) {
251
      if(CSPRep.SRC_DIRECTIVES[i] == aContext) {
252
        return this._directives[aContext].permits(aURI);
253
      }
254
    }
255
    return false;
256
  },
257
258
  /**
259
   * Intersects with another CSPRep, deciding the subset policy 
260
   * that should be enforced, and returning a new instance.
261
   * @param aCSPRep
262
   *        a CSPRep instance to use as "other" CSP
263
   * @returns
264
   *        a new CSPRep instance of the intersection
265
   */
266
  intersectWith:
267
  function cspsd_intersectWith(aCSPRep) {
268
    var newRep = new CSPRep();
269
270
    for(var dir in CSPRep.SRC_DIRECTIVES) {
271
      var dirv = CSPRep.SRC_DIRECTIVES[dir];
272
      newRep._directives[dirv] = this._directives[dirv]
273
               .intersectWith(aCSPRep._directives[dirv]);
274
    }
275
    return newRep;
276
  },
277
278
279
  /**
280
   * Copies default source list to each unspecified directive.
281
   * @returns
282
   *      true  if the makeExplicit succeeds
283
   *      false if it fails (for some weird reason)
284
   */
285
  makeExplicit:
286
  function cspsd_makeExplicit() {
287
    var SD = CSPRep.SRC_DIRECTIVES;
288
    var allowDir = this._directives[SD.ALLOW];
289
    if(!allowDir) {
290
      return false;
291
    }
292
293
    for(var dir in SD) {
294
      var dirv = SD[dir];
295
      if(dirv == SD.ALLOW) {
296
        continue;
297
      }
298
      if(!this._directives[dirv]) {
299
        //implicit directive, make explicit
300
        this._directives[dirv] = allowDir.clone();
301
      }
302
    }
303
    this._isInitialized = true;
304
    return true;
305
  }
306
};
307
308
309
//////////////////////////////////////////////////////////////////////
310
/**
311
 * Class to represent a list of sources 
312
 */
313
function CSPSourceList() {
314
  this._sources = [];
315
  this._permitAllSources = false;
316
}
317
318
/**
319
 * Factory to create a new CSPSourceList, parsed from a string.
320
 *
321
 * @param aStr
322
 *        string rep of a CSP Source List
323
 * @param self (optional)
324
 *        string or CSPSource representing the "self" source
325
 * @returns
326
 *        an instance of CSPSourceList 
327
 */
328
CSPSourceList.fromString = function(aStr, self) {
329
  //Source list is:
330
  //    <host-dir-value> ::= <source-list>
331
  //                       | "none"
332
  //    <source-list>    ::= <source>
333
  //                       | <source-list>" "<source>
334
335
  var slObj = new CSPSourceList();
336
  if(aStr == "none") {
337
    return slObj;
338
  }
339
340
  var ss = aStr.split(/\s+/);
341
  for(var i in ss) {
342
    var o = CSPSource.fromString(ss[i], self);
343
    //make sure none came back undefined
344
    if(!o) { return null; }
345
    slObj._sources[i] = o;
346
  }
347
  return slObj;
348
};
349
350
CSPSourceList.prototype = {
351
352
  /**
353
   * Compares one CSPSourceList to another.
354
   *
355
   * @param that
356
   *        another CSPSourceList
357
   * @returns 
358
   *        true if they have the same data
359
   */
360
  equals:
361
  function(that) {
362
    if(that._sources.length != this._sources.length) {
363
      return false;
364
    }
365
    //sort both arrays and compare like a zipper
366
    //XXX (sid): I think we can make this more efficient
367
    var sortfn = function(a,b) {
368
      return a.toString() > b.toString();
369
    };
370
    var a_sorted = this._sources.sort(sortfn);
371
    var b_sorted = that._sources.sort(sortfn);
372
    for(var i in a_sorted) {
373
      if(!a_sorted[i].equals(b_sorted[i])) {
374
        return false;
375
      }
376
    }
377
    return true;
378
  },
379
380
  /**
381
   * Generates string representation of the Source List.
382
   * Should be fairly similar to the original.
383
   */
384
  toString:
385
  function() {
386
    if(this.isNone()) {
387
      return "none";
388
    }
389
    return this._sources.map(function(x) { return x.toString(); }).join(" ");
390
  },
391
392
  /**
393
   * Returns whether or not this source list represents the "none" special
394
   * case.
395
   */
396
  isNone:
397
  function() {
398
    return (!this._permitAllSources) && (this._sources.length < 1);
399
  },
400
401
  /**
402
   * Makes a new instance that resembles this object.
403
   * @returns
404
   *      a new CSPSourceList
405
   */
406
  clone:
407
  function() {
408
    var aSL = new CSPSourceList();
409
    aSL._permitAllSources = this._permitAllSources;
410
    for(var i in this._sources) {
411
      aSL._sources[i] = this._sources[i].clone();
412
    }
413
    return aSL;
414
  },
415
416
417
  /**
418
   * Determines if this directive accepts a URI.
419
   * @param aURI
420
   *        the URI in question
421
   * @returns
422
   *        true if the URI matches a source in this source list.
423
   */
424
  permits:
425
  function cspsd_matches(aURI) {
426
    if(this.isNone()) {
427
      return false;
428
    }
429
    for(var i in this._sources) {
430
      if(this._sources[i].permits(aURI)) {
431
        return true;
432
      }
433
    }
434
    return false;
435
  },
436
437
438
  /**
439
   * Intersects with another CSPSourceList, deciding the subset directive
440
   * that should be enforced, and returning a new instance.
441
   * @param that 
442
   *        the other CSPSourceList to intersect "this" with
443
   * @returns
444
   *        a new instance of a CSPSourceList representing the intersection
445
   */
446
  intersectWith:
447
  function cspsd_intersectWith(that) {
448
    if(this._permitAllSources) {
449
      return that.clone();
450
    }
451
    if(that._permitAllSources) {
452
      return this.clone();
453
    }
454
455
    // "none" case
456
    if(this.isNone() || that.isNone()) {
457
      return CSPSourceList.fromString("none");
458
    }
459
460
    // --  find only common sources
461
462
    //XXX (sid): figure out a good algorithm for this.
463
    // This is horribly inefficient. O(n^2)
464
    var isrcs = [];
465
    for(var i in this._sources) {
466
      for(var j in that._sources) {
467
        var s = that._sources[j].intersectWith(this._sources[i]);
468
        if(s) {
469
          isrcs.push(s);
470
        }
471
      }
472
    }
473
    //Next, remove duplicates
474
    dup:for(var i = 0; i < isrcs.length; i++) {
475
      for(var j = 0; j < i; j++) {
476
        if(isrcs[i].equals(isrcs[j])) {
477
          isrcs.splice(i, 1);
478
          i--;
479
          continue dup;
480
        }
481
      }
482
    }
483
    var newCSPSrcList = new CSPSourceList();
484
    newCSPSrcList._sources = isrcs;
485
    return newCSPSrcList;
486
  }
487
}
488
489
490
//////////////////////////////////////////////////////////////////////
491
/**
492
 * Class to model a source (scheme, host, port)
493
 */
494
function CSPSource() {
495
  this._scheme = "*";
496
  this._port = "*";
497
  this._host = "*";
498
}
499
500
CSPSource._PORT_RE = /(:(\*|\d+))$/;
501
CSPSource._SCHM_RE = /^((\*|[a-zA-Z][a-zA-Z\+\.\-]*):\/{0,2})/;
502
CSPSource._HOST_RE = /^(localhost|\*|(((\*|[a-zA-Z0-9\-]+)\.)([a-zA-Z0-9\-]+\.)*([a-zA-Z0-9\-]+)))$/;
503
504
/**
505
 * Factory to create a new CSPSource, parsed from a string.
506
 *
507
 * @param aStr
508
 *        string rep of a CSP Source
509
 * @param self (optional)
510
 *        string or CSPSource representing the "self" source
511
 * @returns
512
 *        an instance of CSPSource 
513
 */
514
CSPSource.fromString = function(aStr, self) {
515
  if(aStr == "self") {
516
    if(!self) {
517
      CSPError("self keyword used, but no self data specified");
518
      return null;
519
    } 
520
    if(self == "self") {
521
      CSPError("self reference cannot be 'self'");
522
      return null;
523
    }
524
    if(self instanceof CSPSource) { 
525
      return self.clone();
526
    }
527
    return CSPSource.fromString(self);
528
  }
529
530
  var s = aStr;
531
  var sObj = new CSPSource();
532
533
  if(self && !(self instanceof CSPSource)) {
534
    self = CSPSource.fromString(self);
535
  }
536
537
  // IMPORTANT: scan for port first, because host can be mistaken for scheme
538
  // due to the allowance of dots in the scheme (and a colon before the port)
539
  if(CSPSource._PORT_RE.test(s)) {
540
    sObj._port = s.match(CSPSource._PORT_RE)[0].replace(/:/,'');
541
    s = s.replace(CSPSource._PORT_RE, '');
542
  } else if(self) {
543
    sObj._port = self._port;
544
  }
545
546
  if(CSPSource._SCHM_RE.test(s)) {
547
    sObj._scheme = s.match(CSPSource._SCHM_RE)[0].replace(/[:\/]+/,'');
548
    s = s.replace(CSPSource._SCHM_RE, '');
549
  } else if(self) {
550
    sObj._scheme = self._scheme;
551
  }
552
553
  
554
  if(CSPSource._HOST_RE.test(s)) {
555
    sObj._host = CSPHost.fromString(s.match(CSPSource._HOST_RE)[0]);
556
    if(sObj._host) {
557
      return sObj;
558
    }
559
  } 
560
  CSPError("failed parsing host: '" + s + "'");
561
  return null;
562
};
563
564
CSPSource.prototype = {
565
566
  /**
567
   * Generates string representation of the Source.
568
   * Should be fairly similar to the original.
569
   */
570
  toString:
571
  function() {
572
    var s = this._host;
573
    if(this._port && this._port != "*") {
574
      s = s + ":" + this._port;
575
    }
576
    if(this._scheme && this._scheme != "*") {
577
      s = this._scheme + "://" + s;
578
    }
579
    return s;
580
  },
581
582
  /**
583
   * Makes a new instance that resembles this object.
584
   * @returns
585
   *      a new CSPSource
586
   */
587
  clone:
588
  function() {
589
    var aClone = new CSPSource();
590
    aClone._scheme = this._scheme;
591
    aClone._port = this._port;
592
    aClone._host = this._host.clone();
593
    return aClone;
594
  },
595
596
  /**
597
   * Determines if this Source accepts a URI.
598
   * @param aURI
599
   *        the URI in question
600
   * @returns
601
   *        true if the URI matches a source in this source list.
602
   */
603
  permits:
604
  function(aSource) {
605
    if(!(aSource instanceof CSPSource)) {
606
      //convert to CSPSource first
607
      return this.permits(CSPSource.fromString(aSource));
608
    }
609
610
    //verify scheme, host, and port
611
    if(    (this._scheme == "*" || this._scheme == aSource._scheme)
612
        && (this._port == "*"   || this._port == aSource._port)    ){
613
      return this._host.permits(aSource._host);
614
    }
615
    // something didn't match.
616
    return false;
617
  },
618
619
  /**
620
   * Determines the intersection of two sources.
621
   * Returns a null object if intersection generates no
622
   * hosts that satisfy it.
623
   * @param that 
624
   *        the other CSPSource to intersect "this" with
625
   * @returns
626
   *        a new instance of a CSPSource representing the intersection
627
   */
628
  intersectWith:
629
  function(that) {
630
    var newSource = new CSPSource();
631
632
    //port
633
    if(this._port == "*") { 
634
      newSource._port = that._port;
635
    } else if(that._port == "*") { 
636
      newSource._port = this._port;
637
    } else if(that._port == this._port) {
638
      newSource._port = this._port;
639
    } else {
640
      //CSPError("Could not intersect " + this + " with " + that
641
      //         + " due to port problems.");
642
      return null;
643
    }
644
645
    //scheme
646
    if(this._scheme == "*") { 
647
      newSource._scheme = that._scheme;
648
    } else if(that._scheme == "*") { 
649
      newSource._scheme = this._scheme;
650
    } else if(that._scheme == this._scheme) {
651
      newSource._scheme = this._scheme;
652
    } else {
653
      //CSPError("Could not intersect " + this + " with " + that
654
      //         + " due to scheme problems.");
655
      return null;
656
    }
657
658
    //host
659
    newSource._host = this._host.intersectWith(that._host);
660
661
    return newSource._host ? newSource : null;
662
  },
663
664
  /**
665
   * Compares one CSPSource to another.
666
   *
667
   * @param that
668
   *        another CSPSource
669
   * @returns 
670
   *        true if they have the same data
671
   */
672
  equals:
673
  function(aSrc) {
674
    return this._scheme == aSrc._scheme
675
        && this._port == aSrc._port
676
        && this._host.equals(aSrc._host);
677
  }
678
};
679
  
680
681
//////////////////////////////////////////////////////////////////////
682
/**
683
 * Class to model a host *.x.y.
684
 */
685
function CSPHost() {
686
  this._segments = [];
687
}
688
689
/**
690
 * Factory to create a new CSPHost, parsed from a string.
691
 *
692
 * @param aStr
693
 *        string rep of a CSP Host 
694
 * @returns
695
 *        an instance of CSPHost
696
 */
697
CSPHost.fromString = function(aStr) {
698
  var hObj = new CSPHost();
699
  hObj._segments = aStr.split(/\./);
700
  if(hObj._segments.length < 1 ||
701
     hObj._segments.length == 1 && hObj._segments[0] != "*"
702
                                && hObj._segments[0] != "localhost") {
703
    //only short hosts allowed are "*" and "localhost"
704
    return null;
705
  }
706
  //TODO: double-check that each segment is appropriate.
707
  return hObj;
708
};
709
710
CSPHost.prototype = {
711
  /**
712
   * Generates string representation of the Source.
713
   * Should be fairly similar to the original.
714
   */
715
  toString:
716
  function() {
717
    return this._segments.join(".");
718
  },
719
720
  /**
721
   * Makes a new instance that resembles this object.
722
   * @returns
723
   *      a new CSPHost
724
   */
725
  clone:
726
  function() {
727
    var aHost = new CSPHost();
728
    for(var i in this._segments) {
729
      aHost._segments[i] = this._segments[i];
730
    }
731
    return aHost;
732
  },
733
734
  /**
735
   * Returns true if this host accepts the provided host (or the other way
736
   * around).
737
   * @param aHost
738
   *        the FQDN in question (CSPHost or String)
739
   * @returns
740
   */
741
  permits:
742
  function(aHost) {
743
    if(!(aHost instanceof CSPHost)) {
744
      // -- compare CSPHost to String
745
      return this.permits(CSPHost.fromString(aHost));
746
    }
747
    var thislen = this._segments.length;
748
    var thatlen = aHost._segments.length;
749
750
    // don't accept a less specific host: 
751
    //   \--> *.b.a doesn't accept b.a.
752
    if(thatlen < thislen) { return false; }
753
754
    // check for more specific host (and wildcard):
755
    //   \--> *.b.a accepts d.c.b.a.
756
    //   \--> c.b.a doesn't accept d.c.b.a.
757
    if((thatlen > thislen) && this._segments[0] != "*") {
758
      return false;
759
    }
760
761
    // Given the wildcard condition (from above), 
762
    // only necessary to compare elements that are present
763
    // in this host.  Extra tokens in aHost are ok. 
764
    // * Compare from right to left.
765
    for(var i=1; i <= thislen; i++) {
766
      if( this._segments[thislen-i] != "*" && 
767
          (this._segments[thislen-i] != aHost._segments[thatlen-i]) ) {
768
        return false;
769
      }
770
    }
771
772
    //at this point, all conditions are met, so the host is allowed
773
    return true;
774
  },
775
776
  /**
777
   * Determines the intersection of two Hosts.
778
   * Basically, they must be the same, or one must have a wildcard.
779
   * @param that 
780
   *        the other CSPHost to intersect "this" with
781
   * @returns
782
   *        a new instance of a CSPHost representing the intersection
783
   *        (or null, if they can't be intersected)
784
   */
785
  intersectWith:
786
  function(that) {
787
    if(!(this.permits(that) || that.permits(this))) {
788
      // host definitions cannot co-exist without a more general host
789
      // ... one must be a subset of the other, or intersection makes no sense.
790
      return null;
791
    } 
792
793
    // pick the more specific one, if both are same length.
794
    if(this._segments.length == that._segments.length) {
795
      // *.a vs b.a : b.a
796
      return (this._segments[0] == "*") ? that.clone() : this.clone();
797
    }
798
799
    //different lengths...
800
    // *.b.a vs *.a : *.b.a
801
    // *.b.a vs d.c.b.a : d.c.b.a
802
    return (this._segments.length > that._segments.length) 
803
            ? this.clone() : that.clone();
804
  },
805
806
  /**
807
   * Compares one CSPHost to another.
808
   *
809
   * @param that
810
   *        another CSPHost
811
   * @returns 
812
   *        true if they have the same data
813
   */
814
  equals:
815
  function(that) {
816
    if(this._segments.length != that._segments.length) {
817
      return false;
818
    }
819
    for(var i=0; i<this._segments.length; i++) {
820
      if(this._segments[i] != that._segments[i]) {
821
        return false;
822
      }
823
    }
824
    return true;
825
  }
826
};
(-)a/caps/src/Makefile.in (+9 lines)
Line     Link Here 
 Lines 83-94   endif Link Here 
83
83
84
EXTRA_DSO_LDOPTS = \
84
EXTRA_DSO_LDOPTS = \
85
		$(LIBS_DIR) \
85
		$(LIBS_DIR) \
86
		$(ZLIB_LIBS) \
86
		$(ZLIB_LIBS) \
87
		$(MOZ_JS_LIBS) \
87
		$(MOZ_JS_LIBS) \
88
		$(MOZ_COMPONENT_LIBS) \
88
		$(MOZ_COMPONENT_LIBS) \
89
		$(NULL)
89
		$(NULL)
90
90
91
EXTRA_PP_COMPONENTS = \
92
		contentSecurityPolicy.js \
93
		$(NULL)
94
95
EXTRA_JS_MODULES = \
96
		CSPUtils.jsm \
97
		$(NULL)
98
99
91
include $(topsrcdir)/config/rules.mk
100
include $(topsrcdir)/config/rules.mk
92
101
93
INCLUDES	+= -I$(srcdir)/../include
102
INCLUDES	+= -I$(srcdir)/../include
94
103
(-)b9ad86e82328 (+508 lines)
Added Link Here 
Added Link Here 
1
/* ***** BEGIN LICENSE BLOCK *****
2
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3
 *
4
 * The contents of this file are subject to the Mozilla Public License Version
5
 * 1.1 (the "License"); you may not use this file except in compliance with
6
 * the License. You may obtain a copy of the License at
7
 * http://www.mozilla.org/MPL/
8
 *
9
 * Software distributed under the License is distributed on an "AS IS" basis,
10
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
 * for the specific language governing rights and limitations under the
12
 * License.
13
 *
14
 * The Original Code is the Content Security Policy Data Structures testing code.
15
 *
16
 * The Initial Developer of the Original Code is
17
 * Sid Stamm <sid@mozilla.com>
18
 *
19
 * Contributor(s):
20
 *
21
 * Alternatively, the contents of this file may be used under the terms of
22
 * either the GNU General Public License Version 2 or later (the "GPL"), or
23
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
24
 * in which case the provisions of the GPL or the LGPL are applicable instead
25
 * of those above. If you wish to allow use of your version of this file only
26
 * under the terms of either the GPL or the LGPL, and not to allow others to
27
 * use your version of this file under the terms of the MPL, indicate your
28
 * decision by deleting the provisions above and replace them with the notice
29
 * and other provisions required by the GPL or the LGPL. If you do not delete
30
 * the provisions above, a recipient may use your version of this file under
31
 * the terms of any one of the MPL, the GPL or the LGPL.
32
 *
33
 * ***** END LICENSE BLOCK ***** */
34
35
/**
36
 * Tester for CSPUtils.jsm 
37
 * Launch me with xpcshell.
38
 **/
39
40
//make dump rhino-friendly
41
var dump = print;
42
43
//import "CSPUtils.jsm" ;
44
load('CSPUtils.jsm');
45
load('../../netwerk/test/httpserver/httpd.js');
46
47
var g_SHOW_PASSES = false;
48
var failedTests = [];
49
var g_tests = 0;
50
var g_passed = 0;
51
52
function test(codeThunk, expectedResult) {
53
  var x;
54
  g_tests++;
55
  try {
56
    x = codeThunk();
57
  } catch(e) {
58
    x = e;
59
  }
60
  var success = false;
61
  if(expectedResult == undefined) {
62
    success = (x == undefined);
63
  } else if(expectedResult instanceof Function) {
64
    success = expectedResult(x);
65
  } else {
66
    success = (x == expectedResult);
67
  }
68
69
  if(success) {
70
    if(g_SHOW_PASSES) { print("  pass   :: " + codeThunk.name); }
71
    g_passed++;
72
  } else {
73
    print(" [FAIL]  :: in " + codeThunk.name + ": " + x);
74
    print(x.stack);
75
  }
76
}
77
78
function ASSERT(foo, message) {
79
  if(!foo) {
80
    throw message;
81
  }
82
}
83
84
function ASSERT_NOT_NULL(foo, message) {
85
  if(foo == null) {
86
    throw message;
87
  }
88
}
89
90
function ASSERT_NULL(foo, message) {
91
  if(foo != null) {
92
    throw message;
93
  }
94
}
95
96
// NOTE: this is the object version of ASSERT_EQ
97
function ASSERT_EQUALS(foo, bar, message) {
98
  if(!foo.equals(bar)) {
99
    throw message;
100
  }
101
}
102
103
function ASSERT_EQ(foo, bar, message) {
104
  if(foo != bar) {
105
    throw message;
106
  }
107
}
108
function ASSERT_NEQ(foo, bar, message) {
109
  if(foo == bar) {
110
    throw message;
111
  }
112
}
113
114
function ASSERT_HAS_KEY(foo, key, message) {
115
  for(var x in foo) {
116
    if(x == key) { return; }
117
  }
118
  throw message;
119
}
120
function ASSERT_HAS_VALUE(foo, val, message) {
121
  for(var x in foo) {
122
    if(foo[x] == val) { return; }
123
  }
124
  throw message;
125
}
126
127
// END OF TESTING LIBRARY
128
////////////////////////////////////////////////////////////
129
130
131
132
// Begin Actual Tests
133
134
// setup
135
const POLICY_FROM_URI = "allow self; img-src *";
136
const POLICY_URI = "http://localhost:3400/policy";
137
var httpServer = new nsHttpServer();
138
function policyresponder(request,response) {
139
  response.setStatusLine(request.httpVersion, 200, "OK");
140
  response.setHeader("Content-Type", "text/csp", false);
141
  response.write(POLICY_FROM_URI);
142
}
143
//server.registerDirectory("/", nsILocalFileForBasePath);
144
httpServer.registerPathHandler("/policy", policyresponder);
145
httpServer.start(3400);
146
       
147
148
149
///////// TEST THE CSPHost object ////////////////////////
150
test(
151
  function test_CSPHost_fromstring() {
152
    var h;
153
154
    h = CSPHost.fromString("*");
155
    ASSERT_NOT_NULL(h, "* lone wildcard should work");
156
157
    h = CSPHost.fromString("foo.bar");
158
    ASSERT_NOT_NULL(h, "standard tuple failed");
159
160
    h = CSPHost.fromString("*.bar");
161
    ASSERT_NOT_NULL(h, "wildcard failed");
162
163
    h = CSPHost.fromString("com");
164
    ASSERT_NULL(h, "lone symbol should fail");
165
166
    h = CSPHost.fromString("f00b4r.com");
167
    ASSERT_NOT_NULL(h, "Numbers in hosts should work");
168
169
    h = CSPHost.fromString("foo-bar.com");
170
    ASSERT_NOT_NULL(h, "dashes in hosts should work");
171
172
    // Not enforced in CSPHost
173
    //h = CSPHost.fromString("foo!bar.com");
174
    //ASSERT_NULL(h, "special chars in hosts should fail");
175
  });
176
177
test(
178
  function test_CSPHost_clone() {
179
    h = CSPHost.fromString("*.a.b.c");
180
    h2 = h.clone();
181
    for(var i in h._segments) {
182
      ASSERT_EQ(h._segments[i], h2._segments[i], "cloned segments should match");
183
    }
184
  });
185
186
test(
187
  function test_CSPHost_permits() {
188
    var h = CSPHost.fromString("*.b.c");
189
    var h2 = CSPHost.fromString("a.b.c");
190
    ASSERT( h.permits(h2),      "CSPHost *.b.c should allow CSPHost a.b.c")
191
    ASSERT( h.permits("a.b.c"), "CSPHost *.b.c should allow string a.b.c")
192
    ASSERT(!h.permits("b.c"),   "CSPHost *.b.c should not allow string b.c")
193
    ASSERT(!h.permits("a.a.c"), "CSPHost *.b.c should not allow string a.a.c")
194
195
    ASSERT(!h2.permits(h),      "CSPHost a.b.c should not allow CSPHost *.b.c")
196
    ASSERT(!h2.permits("b.c"),  "CSPHost a.b.c should not allow string b.c")
197
    ASSERT( h2.permits("a.b.c"),"CSPHost a.b.c should allow string a.b.c")
198
  });
199
200
test(
201
    function test_CSPHost_intersectWith() {
202
      var h = CSPHost.fromString("*.b.c");
203
      ASSERT_EQ("*.a.b.c", h.intersectWith(CSPHost.fromString("*.a.b.c")),
204
                "*.a.b.c ^ *.b.c should be *.a.b.c");
205
      ASSERT_NULL(h.intersectWith(CSPHost.fromString("*.d.e")), 
206
                  "*.b.c ^ *.d.e should not work (null)");
207
    });
208
209
///////////////////// Test the Source object //////////////////////
210
211
test(
212
    function test_CSPSource_fromString() {
213
      ASSERT_NOT_NULL(CSPSource.fromString("a.com"),
214
                      "basic source should not be null.");
215
216
      ASSERT_NOT_NULL(CSPSource.fromString("a2-c.com"),
217
                      "ldh characters should all work for host.");
218
219
      ASSERT_NOT_NULL(CSPSource.fromString("*.a.com"),
220
                      "wildcard should work in first token for host.");
221
222
      print(" --- Ignore the following two errors if they print ---");
223
      ASSERT_NULL(CSPSource.fromString("x.*.a.com"),
224
                  "wildcard should not work in non-first token for host.");
225
226
      ASSERT_NULL(CSPSource.fromString("a#2-c.com"),
227
                  "funny characters (#) should not work for host, _HOST_RE is probably broken.");
228
      print(" --- Stop ignoring errors that print ---\n");
229
230
      ASSERT_NOT_NULL(CSPSource.fromString("a.com:23"),
231
                      "failed to parse host with port.");
232
      ASSERT_NOT_NULL(CSPSource.fromString("https://a.com"),
233
                      "failed to parse host with scheme.");
234
      ASSERT_NOT_NULL(CSPSource.fromString("https://a.com:200"),
235
                      "failed to parse host with scheme and port.");
236
    });
237
238
test(
239
    function test_CSPSource_fromString_withSelf() {
240
      var src;
241
      src = CSPSource.fromString("a.com", "https://self.com");
242
      ASSERT(src.permits("https://a.com:443"),
243
             "src should inherit port *");
244
      ASSERT(!src.permits("http://a.com"),
245
              "src should inherit and require https scheme");
246
      ASSERT(src.permits("https://a.com"),
247
             "src should inherit scheme 'https'"); 
248
      
249
      src = CSPSource.fromString("self", "https://self.com");
250
      ASSERT(src.permits("https://self.com:443"),
251
             "src should inherit port *");
252
      ASSERT(!src.permits("http://self.com"),
253
              "src should inherit and require https scheme");
254
      ASSERT(src.permits("https://self.com"),
255
             "src should inherit scheme 'https'"); 
256
      ASSERT(!src.permits("https://a.com"),
257
              "src should reject other hosts"); 
258
    });
259
260
///////////////////// Test the source list //////////////////////
261
262
test(
263
    function test_CSPSourceList_fromString() {
264
      var sd = CSPSourceList.fromString("none");
265
      ASSERT_NOT_NULL(sd, "'none' -- should parse");
266
      ASSERT_EQ(0, sd._sources.length, "'none' should be a zero-length list");
267
268
      sd = CSPSourceList.fromString("*");
269
      ASSERT_EQ(1, sd._sources.length, "'*' should be a one-length list");
270
271
      print(" --- Ignore the following three errors if they print ---");
272
      ASSERT_NULL(CSPSourceList.fromString("f!oo.bar"),
273
                  "funny char in host");
274
      ASSERT_NULL(CSPSourceList.fromString("ht1ps://f-oo.bar"),
275
                  "funny char in scheme");
276
      ASSERT_NULL(CSPSourceList.fromString("https://f-oo.bar:3f"),
277
                  "funny char in port");
278
      print(" --- Stop ignoring errors that print ---\n");
279
    });
280
281
test(
282
    function test_CSPSourceList_fromString_twohost() {
283
      var str = "foo.bar:21 https://ras.bar";
284
      var sd = CSPSourceList.fromString(str);
285
      ASSERT_NOT_NULL(sd, "two-host list should parse");
286
      ASSERT_EQ(2, sd._sources.length, 
287
                "two-host list should parse to two hosts");
288
      ASSERT_EQ(str, sd.toString(),
289
                "two-host list should contain original data");
290
    });
291
292
test(
293
    function test_CSPSourceList_permits() {
294
      var nullSourceList = CSPSourceList.fromString("none");
295
      var simpleSourceList = CSPSourceList.fromString("a.com");
296
      var doubleSourceList = CSPSourceList.fromString("https://foo.com http://bar.com:88");
297
      var allSourceList = CSPSourceList.fromString("*");
298
299
      ASSERT( !nullSourceList.permits("a.com"),
300
              "'none' should permit none.")
301
      ASSERT( simpleSourceList.permits("a.com"),
302
              "a.com should permit a.com");
303
      ASSERT( !simpleSourceList.permits("b.com"),
304
              "wrong host");
305
      ASSERT( doubleSourceList.permits("http://bar.com:88"), 
306
              "double list permits http://bar.com:88");
307
      ASSERT( !doubleSourceList.permits("https://bar.com:88"), 
308
              "double list permits https://bar.com:88");
309
      ASSERT( !doubleSourceList.permits("http://bar.com:443"), 
310
              "double list does not permit http://bar.com:443");
311
      ASSERT( doubleSourceList.permits("https://foo.com:88"), 
312
              "double list permits https://foo.com:88");
313
      ASSERT( !doubleSourceList.permits("foo.com"), 
314
              "double list does not permit foo.com");
315
316
      ASSERT( allSourceList.permits("http://x.com:23"),
317
              "* does not permit specific host");
318
      ASSERT( allSourceList.permits("http://a.b.c.d.e.f.x.com"),
319
              "* does not permit a long host with no port");
320
321
    });
322
323
test(
324
    function test_CSPSourceList_intersect() {
325
      var nullSourceList = CSPSourceList.fromString("none");
326
      var simpleSourceList = CSPSourceList.fromString("a.com");
327
      var doubleSourceList = CSPSourceList.fromString("https://foo.com http://bar.com:88");
328
      var singleFooSourceList = CSPSourceList.fromString("https://foo.com");
329
      var allSourceList = CSPSourceList.fromString("*");
330
331
      ASSERT(nullSourceList.intersectWith(simpleSourceList).isNone(),
332
             "Intersection of one source with 'none' source list should be none.");
333
      ASSERT(nullSourceList.intersectWith(doubleSourceList).isNone(),
334
             "Intersection of two sources with 'none' source list should be none.");
335
      ASSERT(nullSourceList.intersectWith(allSourceList).isNone(),
336
             "Intersection of '*' with 'none' source list should be none.");
337
338
      ASSERT_EQUALS(allSourceList.intersectWith(simpleSourceList), simpleSourceList,
339
             "Intersection of one source with '*' source list should be one source.");
340
      ASSERT_EQUALS(allSourceList.intersectWith(doubleSourceList), doubleSourceList,
341
             "Intersection of two sources with '*' source list should be two sources.");
342
343
      ASSERT(simpleSourceList.intersectWith(doubleSourceList).isNone(),
344
             "Non-overlapping source lists should intersect to 'none'");
345
346
      ASSERT_EQUALS(singleFooSourceList,
347
                    doubleSourceList.intersectWith(singleFooSourceList),
348
                    "subset and superset should intersect to subset.");
349
350
      //TODO: write more tests?
351
352
    });
353
354
///////////////////// Test the Whole CSP rep object //////////////////////
355
356
test(
357
    function test_CSPRep_fromString() {
358
359
      // check default init
360
      //ASSERT(!(new CSPRep())._isInitialized, "Uninitialized rep thinks it is.")
361
362
      var cspr;
363
      var cspr_allowval;
364
365
      // check default policy "allow *"
366
      cspr = CSPRep.fromString("allow *");
367
      ASSERT_HAS_KEY(cspr._directives, CSPRep.SRC_DIRECTIVES.ALLOW,
368
                     "ALLOW directive is missing when specified in fromString");
369
370
      // ... and check that the other directives were auto-filled with the
371
      // ALLOW one.
372
      var SD = CSPRep.SRC_DIRECTIVES;
373
      cspr_allowval = cspr._directives[SD.ALLOW];
374
      for(var d in CSPRep.SRC_DIRECTIVES) {
375
        ASSERT_HAS_KEY(cspr._directives, SD[d], "Missing key " + d);
376
        ASSERT_EQ(cspr._directives[SD[d]].toString(), cspr_allowval.toString(),
377
                  "Implicit directive " + d + " has non-allow value.");
378
      }
379
    });
380
381
382
test(
383
    function test_CSPRep_fromString_oneDir() {
384
385
      var cspr;
386
      var SD = CSPRep.SRC_DIRECTIVES;
387
      var DEFAULTS = [SD.STYLE_SRC, SD.MEDIA_SRC, SD.IMG_SRC,
388
                      SD.FRAME_ANCESTORS, SD.FRAME_SRC];
389
390
      // check one-directive policies
391
      cspr = CSPRep.fromString("allow bar.com; script-src https://foo.com");
392
393
      for(var x in DEFAULTS) {
394
        ASSERT(cspr.permits("http://bar.com:22", DEFAULTS[x]), 
395
              DEFAULTS[x] + " does not use default rule.");
396
        ASSERT(!cspr.permits("https://foo.com:400", DEFAULTS[x]), 
397
              DEFAULTS[x] + " does not use default rule.");
398
      }
399
      ASSERT(!cspr.permits("http://bar.com:22", SD.SCRIPT_SRC), 
400
             "script-src false positive in policy.");
401
      ASSERT(cspr.permits("https://foo.com:400", SD.SCRIPT_SRC), 
402
             "script-src false negative in policy.");
403
    });
404
405
test(
406
    function test_CSPRep_fromString_twodir() {
407
      var cspr;
408
      var SD = CSPRep.SRC_DIRECTIVES;
409
      var DEFAULTS = [SD.STYLE_SRC, SD.MEDIA_SRC, SD.FRAME_ANCESTORS, SD.FRAME_SRC];
410
411
      // check two-directive policies
412
      cspr = CSPRep.fromString("allow allow.com; "
413
                             + "script-src https://foo.com; "
414
                             + "img-src bar.com");
415
      for(var x in DEFAULTS) {
416
        ASSERT(cspr.permits("http://allow.com:22", DEFAULTS[x]), 
417
              DEFAULTS[x] + " does not use default rule.");
418
        ASSERT(!cspr.permits("https://foo.com:400", DEFAULTS[x]), 
419
              DEFAULTS[x] + " does not use default rule.");
420
        ASSERT(!cspr.permits("http://bar.com:400", DEFAULTS[x]), 
421
              DEFAULTS[x] + " does not use default rule.");
422
      }
423
      ASSERT(!cspr.permits("http://allow.com:22", SD.IMG_SRC), 
424
            "img-src does not use default rule.");
425
      ASSERT(!cspr.permits("https://foo.com:400", SD.IMG_SRC), 
426
            "img-src does not use default rule.");
427
      ASSERT(cspr.permits("http://bar.com:400", SD.IMG_SRC), 
428
            "img-src does not use default rule.");
429
430
      ASSERT(!cspr.permits("http://allow.com:22", SD.SCRIPT_SRC), 
431
            "script-src does not use default rule.");
432
      ASSERT(cspr.permits("https://foo.com:400", SD.SCRIPT_SRC), 
433
            "script-src does not use default rule.");
434
      ASSERT(!cspr.permits("http://bar.com:400", SD.SCRIPT_SRC), 
435
            "script-src does not use default rule.");
436
    });
437
438
test(function test_CSPRep_fromString_withself() {
439
       var cspr;
440
       var SD = CSPRep.SRC_DIRECTIVES;
441
       var self = "https://self.com:34";
442
443
       // check one-directive policies
444
       cspr = CSPRep.fromString("allow self; script-src self https://*:*",
445
                                self);
446
      ASSERT(!cspr.permits("https://foo.com:400", SD.IMG_SRC), 
447
             "img-src does not enforce default rule, 'self'.");
448
      ASSERT(cspr.permits(self, SD.IMG_SRC), 
449
             "img-src does not allow self");
450
      ASSERT(!cspr.permits("http://evil.com", SD.SCRIPT_SRC), 
451
             "script-src is too relaxed");
452
      ASSERT(cspr.permits(self, SD.SCRIPT_SRC), 
453
             "script-src should allow self");
454
      ASSERT(cspr.permits("https://evil.com:100", SD.SCRIPT_SRC), 
455
             "script-src is too strict on host/port");
456
     });
457
458
///////////////////// TEST POLICY_URI //////////////////////
459
// TODO: set up a mock server and serve the policy URI
460
test(function test_CSPRep_fromPolicyURI() {
461
        var cspr;
462
        var SD = CSPRep.SRC_DIRECTIVES;
463
        var self = "http://localhost:3400";
464
465
        cspr = CSPRep.fromString("policy-uri " + POLICY_URI, self);
466
        cspr_static = CSPRep.fromString(POLICY_FROM_URI, self);
467
468
        ASSERT_NOT_NULL(cspr, "policy-uri failed to load");
469
470
        // other directives inherit self
471
        for(var i in SD) {
472
          ASSERT_EQUALS(cspr._directives[SD[i]],
473
                        cspr_static._directives[SD[i]],
474
                        SD[i] + " parsed wrong from policy uri");
475
        }
476
    });
477
478
test(function test_CSPRep_fromPolicyURI_failswhenmixed() {
479
        var cspr;
480
        var self = "http://localhost:3400";
481
        var closed_policy = CSPRep.fromString("allow none");
482
        var my_uri_policy = "policy-uri http://localhost:3400/policy";
483
484
        print(" --- Ignore the following two errors if they print ---");
485
        cspr = CSPRep.fromString("allow *; " + my_uri_policy, self);
486
        ASSERT_EQUALS(cspr, closed_policy,
487
                      "Parsing should fail when 'policy-uri' " +
488
                      "is mixed with allow directive");
489
        cspr = CSPRep.fromString("img-src self; " + my_uri_policy, self);
490
        ASSERT_EQUALS(cspr, closed_policy,
491
                      "Parsing should fail when 'policy-uri' " +
492
                      "is mixed with other directives");
493
        print(" --- Stop ignoring errors that print ---\n");
494
495
    });
496
497
498
499
///////////////////// END TESTING //////////////////////
500
501
//teardown
502
httpServer.stop(function() { });
503
504
if(g_passed < g_tests)  {
505
  print(g_tests + " tests run: " + g_passed + " passed, " + (g_tests-g_passed) + " failure(s).");
506
} else {
507
  print("All " + g_tests + " tests passed.");
508
}
(-)b9ad86e82328 (+249 lines)
Added Link Here 
Added Link Here 
1
/* ***** BEGIN LICENSE BLOCK *****
2
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3
 *
4
 * The contents of this file are subject to the Mozilla Public License Version
5
 * 1.1 (the "License"); you may not use this file except in compliance with
6
 * the License. You may obtain a copy of the License at
7
 * http://www.mozilla.org/MPL/
8
 *
9
 * Software distributed under the License is distributed on an "AS IS" basis,
10
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11
 * for the specific language governing rights and limitations under the
12
 * License.
13
 *
14
 * The Original Code is the nsCSPContentPolicy service.
15
 *
16
 * The Initial Developer of the Original Code is
17
 * Sid Stamm <sid@mozilla.com>
18
 *
19
 * Contributor(s):
20
 *   Brandon Sterne <bsterne@mozilla.com>
21
 *
22
 * Alternatively, the contents of this file may be used under the terms of
23
 * either the GNU General Public License Version 2 or later (the "GPL"), or
24
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25
 * in which case the provisions of the GPL or the LGPL are applicable instead
26
 * of those above. If you wish to allow use of your version of this file only
27
 * under the terms of either the GPL or the LGPL, and not to allow others to
28
 * use your version of this file under the terms of the MPL, indicate your
29
 * decision by deleting the provisions above and replace them with the notice
30
 * and other provisions required by the GPL or the LGPL. If you do not delete
31
 * the provisions above, a recipient may use your version of this file under
32
 * the terms of any one of the MPL, the GPL or the LGPL.
33
 *
34
 * ***** END LICENSE BLOCK ***** */
35
36
37
/**
38
 * Content Security Policy
39
 * 
40
 * Overview
41
 * This is a stub component that will be fleshed out to do all the fancy stuff
42
 * that ContentSecurityPolicy has to do.
43
 */
44
45
/* :::::::: Constants and Helpers ::::::::::::::: */
46
47
const Cc = Components.classes;
48
const Ci = Components.interfaces;
49
const Cr = Components.results;
50
const Cu = Components.utils;
51
52
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
53
Cu.import("resource://gre/modules/CSPUtils.jsm");
54
55
function debug(aMsg) {
56
  aMsg = ("CSP: " + aMsg).replace(/\S{80}/g, "$&\n");
57
  dump(aMsg + "\n");
58
  Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService)
59
                                     .logStringMessage(aMsg);
60
}
61
62
/* ::::: Policy Parsing & Data structures :::::: */
63
64
function ContentSecurityPolicy() {
65
  debug("CSP CREATED");
66
  this._isInitialized = false;
67
  this._policy = CSPRep.fromString("allow *");
68
  debug("CSP POLICY INITED TO 'allow *'");
69
}
70
71
/*
72
 * Set up mappings from nsIContentPolicy content types to CSP directives.
73
 */
74
with(Ci.nsIContentPolicy) {
75
  let csp = ContentSecurityPolicy;
76
  let cspr_sd = CSPRep.SRC_DIRECTIVES;
77
78
  csp._MAPPINGS=[];
79
80
  /* default, catch-all case */
81
  csp._MAPPINGS[TYPE_OTHER]             =  cspr_sd.ALLOW;
82
83
  /* self */
84
  csp._MAPPINGS[TYPE_DOCUMENT]          =  null;
85
86
  /* shouldn't see this one */
87
  csp._MAPPINGS[TYPE_REFRESH]           =  null;
88
89
  /* categorized content types */
90
  csp._MAPPINGS[TYPE_SCRIPT]            = cspr_sd.SCRIPT_SRC;
91
  csp._MAPPINGS[TYPE_IMAGE]             = cspr_sd.IMG_SRC;
92
  csp._MAPPINGS[TYPE_STYLESHEET]        = cspr_sd.STYLE_SRC;
93
  csp._MAPPINGS[TYPE_OBJECT]            = cspr_sd.OBJECT_SRC;
94
  csp._MAPPINGS[TYPE_SUBDOCUMENT]       = cspr_sd.FRAME_SRC;
95
  csp._MAPPINGS[TYPE_MEDIA]             = cspr_sd.MEDIA_SRC;
96
97
98
  /* These must go through the catch-all */
99
  csp._MAPPINGS[TYPE_XBL]               = cspr_sd.ALLOW;
100
  csp._MAPPINGS[TYPE_PING]              = cspr_sd.ALLOW;
101
  csp._MAPPINGS[TYPE_XMLHTTPREQUEST]    = cspr_sd.ALLOW;
102
  csp._MAPPINGS[TYPE_OBJECT_SUBREQUEST] = cspr_sd.ALLOW;
103
  csp._MAPPINGS[TYPE_DTD]               = cspr_sd.ALLOW;
104
  csp._MAPPINGS[TYPE_FONT]              = cspr_sd.ALLOW;
105
}
106
107
ContentSecurityPolicy.prototype = {
108
  classDescription: "Content Security Policy Component",
109
  contractID:       "@mozilla.org/contentsecuritypolicy;1",
110
  classID:          Components.ID("{AB36A2BF-CB32-4AA6-AB41-6B4E4444A221}"),
111
  QueryInterface:   XPCOMUtils.generateQI([Ci.IContentSecurityPolicy]),
112
113
  // get this contractID registered for certain categories via XPCOMUtils
114
  _xpcom_categories: [ ],
115
116
  get isInitialized() {
117
    return this._isInitialized;
118
  },
119
120
  set isInitialized (foo) {
121
    this._isInitialized = foo;
122
  },
123
124
  get policy () {
125
    return this._policy.toString();
126
  },
127
128
  /*
129
  // Having a setter is a bad idea... opens up the policy to "loosening"
130
  // Instead, use "refinePolicy."
131
  set policy (aStr) {
132
    this._policy = CSPRep.fromString(aStr);
133
  },
134
  */
135
136
/* ........ Methods .............. */
137
138
  /**
139
   * Given a new policy, intersects the currently enforced policy with the new
140
   * one and stores the result.  The effect is a "tightening" or refinement of
141
   * an old policy.  This is called any time a new policy is encountered and
142
   * the effective policy has to be refined.
143
   */
144
  refinePolicy:
145
  function csp_refinePolicy(aString) {
146
    debug("REFINE POLICY: " + aString);
147
148
    // stay uninitialized until policy merging is done
149
    this._isInitialized = false;
150
151
    // If there is a policy-uri, fetch the policy, then re-call this function.
152
    // (1) parse and create a CSPRep object
153
    var newpolicy = CSPRep.fromString(aString);
154
155
    // (2) Intersect the currently installed CSPRep object with the new one
156
    var intersect = this._policy.intersectWith(newpolicy);
157
    //TODO: make sure intersect worked
158
 
159
    // (3) Save the result
160
    this._policy = intersect;
161
    this._isInitialized = true;
162
  },
163
164
  /**
165
   * Generates and sends a violation report to the specified report URIs.
166
   */
167
  sendReports:
168
  function(blockedUri, victimContext, violatedDirective) {
169
    var uris = this._policy.getReportURIs();
170
    if(uris.length > 0) {
171
      //TODO: generate report to send
172
      //   ... composed of:
173
      // <csp-report>
174
      //   <request>GET /index.html HTTP/1.1</request>
175
      //   <request-headers>Host: example.com
176
      //            User-Agent: ...
177
      //            ...
178
      //   </request-headers>
179
      //   <blocked-uri>...</blocked-uri>
180
      //   <violated-directive>...</violated-directive>
181
      // </csp-report>
182
      //   
183
     
184
      for(let i in uris) {
185
        //TODO: asynchronously send report to uris[i]
186
      }
187
    }
188
  },
189
190
  /**
191
   * Delegate method called by the service when sub-elements of the protected
192
   * document are being loaded.  Given a bit of information about the request,
193
   * decides whether or not the policy is satisfied.
194
   */
195
  shouldLoad:
196
  function csp_shouldLoad(aContentType, 
197
                          aContentLocation, 
198
                          aRequestOrigin, 
199
                          aContext, 
200
                          aMimeTypeGuess, 
201
                          aExtra) {
202
203
    //interpret the context, and then pass off to the decision structure
204
    //debug("shouldLoad content type =" + aContentType);
205
    var cspContext = ContentSecurityPolicy._MAPPINGS[aContentType];
206
    //debug("shouldLoad CSP directive =" + cspContext);
207
208
    // if the mapping is null, there's no policy, let it through.
209
    if(!cspContext) {
210
      return Ci.nsIContentPolicy.ACCEPT;
211
    }
212
213
    // otherwise, honor the translation
214
    var source = aContentLocation.scheme + "://" + aContentLocation.hostPort; 
215
    var res = this._policy.permits(source, cspContext)
216
              ? Ci.nsIContentPolicy.ACCEPT 
217
              : Ci.nsIContentPolicy.REJECT_SERVER;
218
219
    // TODO: frame-ancestors
220
221
    // If the result is *NOT* ACCEPT, then send report
222
    if(res != Ci.nsIContentPolicy.ACCEPT) { 
223
      debug("blocking request for " + aContentLocation.asciiSpec);
224
      this.sendReports(aContentLocation,
225
                       aContext,
226
                       cspContext + " " + this._policy._directives[cspContext]);
227
    }
228
229
    return res;
230
  },
231
  
232
  shouldProcess:
233
  function csp_shouldProcess(aContentType,
234
                             aContentLocation,
235
                             aRequestOrigin,
236
                             aContext,
237
                             aMimeType,
238
                             aExtra) {
239
    //TODO: do we need this?
240
    debug("shouldProcess aContext=" + aContext);
241
    return true;
242
  },
243
244
};
245
246
247
248
function NSGetModule(aComMgr, aFileSpec)
249
  XPCOMUtils.generateModule([ContentSecurityPolicy]);
(-)a/content/base/public/nsIDocument.h (+12 lines)
Line     Link Here 
 Lines 52-67    Link Here 
52
#include "nsCompatibility.h"
52
#include "nsCompatibility.h"
53
#include "nsTObserverArray.h"
53
#include "nsTObserverArray.h"
54
#include "nsTHashtable.h"
54
#include "nsTHashtable.h"
55
#include "nsHashKeys.h"
55
#include "nsHashKeys.h"
56
#include "nsNodeInfoManager.h"
56
#include "nsNodeInfoManager.h"
57
#include "nsIStreamListener.h"
57
#include "nsIStreamListener.h"
58
#include "nsIObserver.h"
58
#include "nsIObserver.h"
59
#include "nsAutoPtr.h"
59
#include "nsAutoPtr.h"
60
#include "IContentSecurityPolicy.h"
60
#ifdef MOZ_SMIL
61
#ifdef MOZ_SMIL
61
class nsSMILAnimationController;
62
class nsSMILAnimationController;
62
#endif // MOZ_SMIL
63
#endif // MOZ_SMIL
63
64
64
class nsIContent;
65
class nsIContent;
65
class nsPresContext;
66
class nsPresContext;
66
class nsIPresShell;
67
class nsIPresShell;
67
class nsIDocShell;
68
class nsIDocShell;
 Lines 1063-1078   public: Link Here 
1063
                    "have a presentation or a docshell or a window");
1064
                    "have a presentation or a docshell or a window");
1064
    NS_PRECONDITION(aDisplayDocument != this, "Should be different document");
1065
    NS_PRECONDITION(aDisplayDocument != this, "Should be different document");
1065
    NS_PRECONDITION(!aDisplayDocument->GetDisplayDocument(),
1066
    NS_PRECONDITION(!aDisplayDocument->GetDisplayDocument(),
1066
                    "Display documents should not nest");
1067
                    "Display documents should not nest");
1067
    mDisplayDocument = aDisplayDocument;
1068
    mDisplayDocument = aDisplayDocument;
1068
  }
1069
  }
1069
1070
1070
  /**
1071
  /**
1072
   * Return the Content Security Policy object for the document
1073
   */
1074
  IContentSecurityPolicy* GetContentSecurityPolicy() const
1075
  {
1076
    return mCSP;
1077
  }
1078
1079
  /**
1071
   * A class that represents an external resource load that has begun but
1080
   * A class that represents an external resource load that has begun but
1072
   * doesn't have a document yet.  Observers can be registered on this object,
1081
   * doesn't have a document yet.  Observers can be registered on this object,
1073
   * and will be notified after the document is created.  Observers registered
1082
   * and will be notified after the document is created.  Observers registered
1074
   * after the document has been created will NOT be notified.  When observers
1083
   * after the document has been created will NOT be notified.  When observers
1075
   * are notified, the subject will be the newly-created document, the topic
1084
   * are notified, the subject will be the newly-created document, the topic
1076
   * will be "external-resource-document-created", and the data will be null.
1085
   * will be "external-resource-document-created", and the data will be null.
1077
   * If document creation fails for some reason, observers will still be
1086
   * If document creation fails for some reason, observers will still be
1078
   * notified, with a null document pointer.
1087
   * notified, with a null document pointer.
 Lines 1264-1279   protected: Link Here 
1264
  PRUint32            mSubtreeModifiedDepth;
1273
  PRUint32            mSubtreeModifiedDepth;
1265
1274
1266
  // If we're an external resource document, this will be non-null and will
1275
  // If we're an external resource document, this will be non-null and will
1267
  // point to our "display document": the one that all resource lookups should
1276
  // point to our "display document": the one that all resource lookups should
1268
  // go to.
1277
  // go to.
1269
  nsCOMPtr<nsIDocument> mDisplayDocument;
1278
  nsCOMPtr<nsIDocument> mDisplayDocument;
1270
1279
1271
  PRUint32 mEventsSuppressed;
1280
  PRUint32 mEventsSuppressed;
1281
1282
  // CSP policy object/data
1283
  nsCOMPtr<IContentSecurityPolicy> mCSP;
1272
};
1284
};
1273
1285
1274
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
1286
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
1275
1287
1276
/**
1288
/**
1277
 * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
1289
 * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
1278
 * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
1290
 * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
1279
 * object is deleted.
1291
 * object is deleted.
(-)a/content/base/src/Makefile.in (+1 lines)
Line     Link Here 
 Lines 116-131   CPPSRCS = \ Link Here 
116
		nsContentAreaDragDrop.cpp \
116
		nsContentAreaDragDrop.cpp \
117
		nsContentIterator.cpp \
117
		nsContentIterator.cpp \
118
		nsContentList.cpp \
118
		nsContentList.cpp \
119
		nsContentPolicy.cpp \
119
		nsContentPolicy.cpp \
120
		nsContentSink.cpp \
120
		nsContentSink.cpp \
121
		nsContentUtils.cpp \
121
		nsContentUtils.cpp \
122
		nsCopySupport.cpp \
122
		nsCopySupport.cpp \
123
		nsCrossSiteListenerProxy.cpp \
123
		nsCrossSiteListenerProxy.cpp \
124
		nsCSPService.cpp \
124
		nsDataDocumentContentPolicy.cpp \
125
		nsDataDocumentContentPolicy.cpp \
125
		nsDOMAttribute.cpp \
126
		nsDOMAttribute.cpp \
126
		nsDOMAttributeMap.cpp \
127
		nsDOMAttributeMap.cpp \
127
		nsDOMDocumentType.cpp \
128
		nsDOMDocumentType.cpp \
128
		nsDOMFile.cpp \
129
		nsDOMFile.cpp \
129
		nsDOMLists.cpp \
130
		nsDOMLists.cpp \
130
		nsDOMParser.cpp \
131
		nsDOMParser.cpp \
131
		nsDOMSerializer.cpp \
132
		nsDOMSerializer.cpp \
(-)b9ad86e82328 (+135 lines)
Added Link Here 
Added Link Here 
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4
 *
5
 * The contents of this file are subject to the Mozilla Public License Version
6
 * 1.1 (the "License"); you may not use this file except in compliance with
7
 * the License. You may obtain a copy of the License at
8
 * http://www.mozilla.org/MPL/
9
 *
10
 * Software distributed under the License is distributed on an "AS IS" basis,
11
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
 * for the specific language governing rights and limitations under the
13
 * License.
14
 *
15
 * The Original Code is mozilla.org code.
16
 *
17
 * The Initial Developer of the Original Code is
18
 * Mozilla Corporation.
19
 * Portions created by the Initial Developer are Copyright (C) 2009
20
 * the Initial Developer. All Rights Reserved.
21
 *
22
 * Contributor(s):
23
 *   Brandon Sterne <bsterne@mozilla.com>
24
 *
25
 * Alternatively, the contents of this file may be used under the terms of
26
 * either of the GNU General Public License Version 2 or later (the "GPL"),
27
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
 * in which case the provisions of the GPL or the LGPL are applicable instead
29
 * of those above. If you wish to allow use of your version of this file only
30
 * under the terms of either the GPL or the LGPL, and not to allow others to
31
 * use your version of this file under the terms of the MPL, indicate your
32
 * decision by deleting the provisions above and replace them with the notice
33
 * and other provisions required by the GPL or the LGPL. If you do not delete
34
 * the provisions above, a recipient may use your version of this file under
35
 * the terms of any one of the MPL, the GPL or the LGPL.
36
 *
37
 * ***** END LICENSE BLOCK ***** */
38
39
#include "nsIURI.h"
40
#include "nsString.h"
41
#include "nsCSPService.h"
42
#include "nsIDocument.h"
43
#include "nsIContent.h"
44
#include "IContentSecurityPolicy.h"
45
46
CSPService::CSPService()
47
{
48
}
49
50
CSPService::~CSPService()
51
{
52
}
53
54
NS_IMPL_ISUPPORTS1(CSPService, nsIContentPolicy)
55
56
/* nsIContentPolicy implementation */
57
NS_IMETHODIMP
58
CSPService::ShouldLoad(PRUint32 aContentType,
59
                       nsIURI *aContentLocation,
60
                       nsIURI *aRequestOrigin,
61
                       nsISupports *aRequestContext,
62
                       const nsACString &aMimeTypeGuess,
63
                       nsISupports *aExtra,
64
                       PRInt16 *aDecision)
65
{
66
  if (!aContentLocation)
67
    return NS_ERROR_FAILURE;
68
69
  /*
70
#ifdef DEBUG
71
  {
72
    nsCAutoString location("None");
73
    aContentLocation->GetSpec(location);
74
    printf(" *** CSPService::ShouldLoad called for %s\n",
75
           location.get());
76
  }
77
#endif
78
  */
79
80
  // find the nsDocument that initiated this request and see if it has a
81
  // CSP policy object
82
  nsCOMPtr<nsIDocument> doc;
83
  nsCOMPtr<nsIContent> node(do_QueryInterface(aRequestContext));
84
  if (node) {
85
    doc = node->GetOwnerDoc();
86
  }
87
  if (!doc) {
88
    doc = do_QueryInterface(aRequestContext);
89
  }
90
  
91
  // default decision, CSP can revise it if there's a policy to enforce
92
  *aDecision = nsIContentPolicy::ACCEPT;
93
  if (doc) {
94
    IContentSecurityPolicy* csp = doc->GetContentSecurityPolicy();
95
    if (csp) {
96
#ifdef DEBUG
97
      nsAutoString policy;
98
      csp->GetPolicy(policy);
99
      printf(" *** CSPService - document has policy: %s\n",
100
              ToNewCString(policy));
101
#endif
102
      // obtain the enforcement decision
103
      csp->ShouldLoad(aContentType,
104
                      aContentLocation,
105
                      aRequestOrigin,
106
                      aRequestContext,
107
                      aMimeTypeGuess,
108
                      aExtra,
109
                      aDecision);
110
    }
111
  }
112
#ifdef DEBUG
113
  else {
114
    nsCAutoString uriSpec("None");
115
    aContentLocation->GetSpec(uriSpec);
116
    printf(" *** CSPService - COULD NOT get nsIDocument for location: %s\n",
117
            uriSpec.get());
118
  }
119
#endif
120
	
121
  return NS_OK;
122
}
123
124
NS_IMETHODIMP
125
CSPService::ShouldProcess(PRUint32         aContentType,
126
                          nsIURI           *aContentLocation,
127
                          nsIURI           *aRequestOrigin,
128
                          nsISupports      *aRequestContext,
129
                          const nsACString &aMimeTypeGuess,
130
                          nsISupports      *aExtra,
131
                          PRInt16          *aDecision)
132
{
133
  *aDecision = nsIContentPolicy::ACCEPT;
134
  return NS_OK;
135
}
(-)b9ad86e82328 (+67 lines)
Added Link Here 
Added Link Here 
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4
 *
5
 * The contents of this file are subject to the Mozilla Public License Version
6
 * 1.1 (the "License"); you may not use this file except in compliance with
7
 * the License. You may obtain a copy of the License at
8
 * http://www.mozilla.org/MPL/
9
 *
10
 * Software distributed under the License is distributed on an "AS IS" basis,
11
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
 * for the specific language governing rights and limitations under the
13
 * License.
14
 *
15
 * The Original Code is mozilla.org code.
16
 *
17
 * The Initial Developer of the Original Code is
18
 * Mozilla Corporation.
19
 * Portions created by the Initial Developer are Copyright (C) 2009
20
 * the Initial Developer. All Rights Reserved.
21
 *
22
 * Contributor(s):
23
 *   Brandon Sterne <bsterne@mozilla.com>
24
 *
25
 * Alternatively, the contents of this file may be used under the terms of
26
 * either of the GNU General Public License Version 2 or later (the "GPL"),
27
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
 * in which case the provisions of the GPL or the LGPL are applicable instead
29
 * of those above. If you wish to allow use of your version of this file only
30
 * under the terms of either the GPL or the LGPL, and not to allow others to
31
 * use your version of this file under the terms of the MPL, indicate your
32
 * decision by deleting the provisions above and replace them with the notice
33
 * and other provisions required by the GPL or the LGPL. If you do not delete
34
 * the provisions above, a recipient may use your version of this file under
35
 * the terms of any one of the MPL, the GPL or the LGPL.
36
 *
37
 * ***** END LICENSE BLOCK ***** */
38
39
#include "nsIGenericFactory.h"
40
41
#include "nsCOMPtr.h"
42
#include "nsXPCOM.h"
43
#include "nsIServiceManager.h"
44
#include "nsICategoryManager.h"
45
46
#include "nsMemory.h"
47
48
#include "nsIContentPolicy.h"
49
#include "nsIObserver.h"
50
51
#define CSPSERVICE_CONTRACTID "@mozilla.org/cspservice;1"
52
#define CSPSERVICE_CID \
53
{ 0x8d2f40b2, 0x4875, 0x4c95, \
54
  { 0x97, 0xd9, 0x3f, 0x7d, 0xca, 0x2c, 0xb4, 0x60 } }
55
56
class CSPService : public nsIContentPolicy
57
{
58
public:
59
  NS_DECL_ISUPPORTS
60
  NS_DECL_NSICONTENTPOLICY
61
  
62
  CSPService();
63
  virtual ~CSPService();
64
65
private:
66
  PRBool mEnabled;
67
};
(-)a/content/base/src/nsDocument.cpp (+51 lines)
Line     Link Here 
 Lines 173-188   static NS_DEFINE_CID(kDOMEventGroupCID, Link Here 
173
#include "mozAutoDocUpdate.h"
173
#include "mozAutoDocUpdate.h"
174
174
175
#ifdef MOZ_SMIL
175
#ifdef MOZ_SMIL
176
#include "nsSMILAnimationController.h"
176
#include "nsSMILAnimationController.h"
177
#include "imgIContainer.h"
177
#include "imgIContainer.h"
178
#include "nsSVGUtils.h"
178
#include "nsSVGUtils.h"
179
#endif // MOZ_SMIL
179
#endif // MOZ_SMIL
180
180
181
// FOR CSP (autogenerated by xpidl)
182
#include "IContentSecurityPolicy.h"
183
181
184
182
#ifdef MOZ_LOGGING
185
#ifdef MOZ_LOGGING
183
// so we can get logging even in release builds
186
// so we can get logging even in release builds
184
#define FORCE_PR_LOG 1
187
#define FORCE_PR_LOG 1
185
#endif
188
#endif
186
#include "prlog.h"
189
#include "prlog.h"
187
190
188
#ifdef PR_LOGGING
191
#ifdef PR_LOGGING
 Lines 2217-2232   nsDocument::StartDocumentLoad(const char Link Here 
2217
    semicolon = start;
2220
    semicolon = start;
2218
    FindCharInReadable(';', semicolon, end);
2221
    FindCharInReadable(';', semicolon, end);
2219
    mContentType = Substring(start, semicolon);
2222
    mContentType = Substring(start, semicolon);
2220
  }
2223
  }
2221
2224
2222
  RetrieveRelevantHeaders(aChannel);
2225
  RetrieveRelevantHeaders(aChannel);
2223
2226
2224
  mChannel = aChannel;
2227
  mChannel = aChannel;
2228
  
2229
// --------------  Initialize CSP
2230
  nsresult rv;
2231
  PRBool system;
2232
  nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
2233
  nsAutoString cspHeaderValue;
2234
  nsIAtom* cspHeaderAtom = NS_NewAtom("x-content-security-policy");
2235
2236
  this->GetHeaderData(cspHeaderAtom, cspHeaderValue);
2237
2238
  if(NS_SUCCEEDED(ssm->IsSystemPrincipal(NodePrincipal(), &system)) && system) {
2239
    //only makes sense to register new CSP if this document is not priviliged
2240
    return NS_OK;
2241
  }
2242
2243
  if(cspHeaderValue.IsEmpty()) {
2244
    // no CSP header present
2245
    return NS_OK;
2246
  }
2247
2248
#ifdef DEBUG
2249
  printf(" >> *** DOCUMENT specified CSP header!\n");
2250
#endif
2251
2252
  mCSP = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv);
2253
2254
  if(NS_FAILED(rv)) {
2255
#ifdef DEBUG
2256
    printf("failed to create CSP object: %6x", rv);
2257
#endif
2258
    return rv;
2259
  }
2260
2261
#ifdef DEBUG
2262
  printf(" >> *** CSP Created \"%s\"\n", ToNewCString(cspHeaderValue));
2263
#endif
2264
2265
  // Also start parsing the policy
2266
  mCSP->RefinePolicy(cspHeaderValue);
2267
2268
#ifdef DEBUG
2269
  printf(" >> *** CSP refined.");
2270
#endif
2271
// -------------- CSP initialized
2225
2272
2226
  return NS_OK;
2273
  return NS_OK;
2227
}
2274
}
2228
2275
2229
void
2276
void
2230
nsDocument::StopDocumentLoad()
2277
nsDocument::StopDocumentLoad()
2231
{
2278
{
2232
  if (mParser) {
2279
  if (mParser) {
 Lines 6561-6576   nsDocument::RetrieveRelevantHeaders(nsIC Link Here 
6561
6608
6562
    static const char *const headers[] = {
6609
    static const char *const headers[] = {
6563
      "default-style",
6610
      "default-style",
6564
      "content-style-type",
6611
      "content-style-type",
6565
      "content-language",
6612
      "content-language",
6566
      "content-disposition",
6613
      "content-disposition",
6567
      "refresh",
6614
      "refresh",
6568
      "x-dns-prefetch-control",
6615
      "x-dns-prefetch-control",
6616
      "x-content-security-policy",
6569
      // add more http headers if you need
6617
      // add more http headers if you need
6570
      // XXXbz don't add content-location support without reading bug
6618
      // XXXbz don't add content-location support without reading bug
6571
      // 238654 and its dependencies/dups first.
6619
      // 238654 and its dependencies/dups first.
6572
      0
6620
      0
6573
    };
6621
    };
6574
    
6622
    
6575
    nsCAutoString headerVal;
6623
    nsCAutoString headerVal;
6576
    const char *const *name = headers;
6624
    const char *const *name = headers;
 Lines 6864-6879   nsDocument::CanSavePresentation(nsIReque Link Here 
6864
6912
6865
void
6913
void
6866
nsDocument::Destroy()
6914
nsDocument::Destroy()
6867
{
6915
{
6868
  // The ContentViewer wants to release the document now.  So, tell our content
6916
  // The ContentViewer wants to release the document now.  So, tell our content
6869
  // to drop any references to the document so that it can be destroyed.
6917
  // to drop any references to the document so that it can be destroyed.
6870
  if (mIsGoingAway)
6918
  if (mIsGoingAway)
6871
    return;
6919
    return;
6920
	
6921
  // Take down CSP
6922
  mCSP = 0;
6872
6923
6873
  mIsGoingAway = PR_TRUE;
6924
  mIsGoingAway = PR_TRUE;
6874
6925
6875
  RemovedFromDocShell();
6926
  RemovedFromDocShell();
6876
6927
6877
  PRUint32 i, count = mChildren.ChildCount();
6928
  PRUint32 i, count = mChildren.ChildCount();
6878
  for (i = 0; i < count; ++i) {
6929
  for (i = 0; i < count; ++i) {
6879
    mChildren.ChildAt(i)->DestroyContent();
6930
    mChildren.ChildAt(i)->DestroyContent();
(-)a/layout/build/nsLayoutModule.cpp (+80 lines)
Line     Link Here 
 Lines 266-281   static nsresult Initialize(nsIModule* aS Link Here 
266
static void Shutdown();
266
static void Shutdown();
267
267
268
#ifdef MOZ_XTF
268
#ifdef MOZ_XTF
269
#include "nsIXTFService.h"
269
#include "nsIXTFService.h"
270
#include "nsIXMLContentBuilder.h"
270
#include "nsIXMLContentBuilder.h"
271
#endif
271
#endif
272
272
273
#include "nsGeolocation.h"
273
#include "nsGeolocation.h"
274
#include "nsCSPService.h"
274
275
275
// Transformiix
276
// Transformiix
276
/* {0C351177-0159-4500-86B0-A219DFDE4258} */
277
/* {0C351177-0159-4500-86B0-A219DFDE4258} */
277
#define TRANSFORMIIX_XPATH1_SCHEME_CID \
278
#define TRANSFORMIIX_XPATH1_SCHEME_CID \
278
{ 0xc351177, 0x159, 0x4500, { 0x86, 0xb0, 0xa2, 0x19, 0xdf, 0xde, 0x42, 0x58 } }
279
{ 0xc351177, 0x159, 0x4500, { 0x86, 0xb0, 0xa2, 0x19, 0xdf, 0xde, 0x42, 0x58 } }
279
280
280
/* 5d5d92cd-6bf8-11d9-bf4a-000a95dc234c */
281
/* 5d5d92cd-6bf8-11d9-bf4a-000a95dc234c */
281
#define TRANSFORMIIX_NODESET_CID \
282
#define TRANSFORMIIX_NODESET_CID \
 Lines 843-858   CreateWindowControllerWithSingletonComma Link Here 
843
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMScriptObjectFactory)
844
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMScriptObjectFactory)
844
NS_GENERIC_FACTORY_CONSTRUCTOR(nsBaseDOMException)
845
NS_GENERIC_FACTORY_CONSTRUCTOR(nsBaseDOMException)
845
846
846
#define NS_GEOLOCATION_SERVICE_CID \
847
#define NS_GEOLOCATION_SERVICE_CID \
847
  { 0x404d02a, 0x1CA, 0xAAAB, { 0x47, 0x62, 0x94, 0x4b, 0x1b, 0xf2, 0xf7, 0xb5 } }
848
  { 0x404d02a, 0x1CA, 0xAAAB, { 0x47, 0x62, 0x94, 0x4b, 0x1b, 0xf2, 0xf7, 0xb5 } }
848
849
849
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsGeolocationService, nsGeolocationService::GetGeolocationService)
850
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsGeolocationService, nsGeolocationService::GetGeolocationService)
850
851
852
static NS_METHOD
853
CSPServiceRegistration(nsIComponentManager *aCompMgr,
854
                       nsIFile *aPath,
855
                       const char *registryLocation,
856
                       const char *componentType,
857
                       const nsModuleComponentInfo *info)
858
{
859
  nsresult rv;
860
  nsCOMPtr<nsIServiceManager> servman = do_QueryInterface((nsISupports*)aCompMgr, &rv);
861
  if (NS_FAILED(rv))
862
    return rv;
863
864
  nsCOMPtr<nsICategoryManager> catman;
865
  rv = servman->GetServiceByContractID(NS_CATEGORYMANAGER_CONTRACTID,
866
                                       NS_GET_IID(nsICategoryManager),
867
                                       getter_AddRefs(catman));
868
  if (NS_FAILED(rv))
869
    return rv;
870
  
871
  char* previous = nsnull;
872
  rv = catman->AddCategoryEntry("xpcom-startup",
873
                                "CSPService",
874
                                CSPSERVICE_CONTRACTID,
875
                                PR_TRUE,
876
                                PR_TRUE,
877
                                &previous);
878
  if (previous)
879
    nsMemory::Free(previous);
880
881
  rv = catman->AddCategoryEntry("content-policy",
882
                                "CSPService",
883
                                CSPSERVICE_CONTRACTID,
884
                                PR_TRUE,
885
                                PR_TRUE,
886
                                &previous);
887
  if (previous)
888
    nsMemory::Free(previous);
889
890
  return rv;
891
}
892
893
static NS_METHOD
894
CSPServiceUnregistration(nsIComponentManager *aCompMgr,
895
                         nsIFile *aPath,
896
                         const char *registryLocation,
897
                         const nsModuleComponentInfo *info){
898
  nsresult rv;
899
900
  nsCOMPtr<nsIServiceManager> servman = do_QueryInterface((nsISupports*)aCompMgr, &rv);
901
  if (NS_FAILED(rv))
902
    return rv;
903
904
  nsCOMPtr<nsICategoryManager> catman;
905
  rv = servman->GetServiceByContractID(NS_CATEGORYMANAGER_CONTRACTID,
906
                                       NS_GET_IID(nsICategoryManager),
907
                                       getter_AddRefs(catman));
908
  if (NS_FAILED(rv))
909
    return rv;
910
911
  rv = catman->DeleteCategoryEntry("xpcom-startup",
912
                                   "CSPService",
913
                                   PR_TRUE);
914
915
  rv = catman->DeleteCategoryEntry("content-policy",
916
                                   "CSPService",
917
                                   PR_TRUE);
918
919
  return rv;
920
}
921
922
NS_GENERIC_FACTORY_CONSTRUCTOR(CSPService)
923
851
// The list of components we register
924
// The list of components we register
852
static const nsModuleComponentInfo gComponents[] = {
925
static const nsModuleComponentInfo gComponents[] = {
853
#ifdef DEBUG
926
#ifdef DEBUG
854
  { "Frame utility",
927
  { "Frame utility",
855
    NS_FRAME_UTIL_CID,
928
    NS_FRAME_UTIL_CID,
856
    nsnull,
929
    nsnull,
857
    CreateNewFrameUtil },
930
    CreateNewFrameUtil },
858
  { "Layout debugger",
931
  { "Layout debugger",
 Lines 1446-1461   static const nsModuleComponentInfo gComp Link Here 
1446
      nsTextServicesDocumentConstructor },
1519
      nsTextServicesDocumentConstructor },
1447
#endif
1520
#endif
1448
1521
1449
    { "Geolocation Service",
1522
    { "Geolocation Service",
1450
      NS_GEOLOCATION_SERVICE_CID,
1523
      NS_GEOLOCATION_SERVICE_CID,
1451
      "@mozilla.org/geolocation/service;1",
1524
      "@mozilla.org/geolocation/service;1",
1452
      nsGeolocationServiceConstructor },
1525
      nsGeolocationServiceConstructor },
1453
1526
1527
    { "Content Security Policy Service",
1528
      CSPSERVICE_CID,
1529
      CSPSERVICE_CONTRACTID,
1530
      CSPServiceConstructor,
1531
      CSPServiceRegistration,
1532
      CSPServiceUnregistration
1533
    },
1454
1534
1455
1535
1456
  { "Focus Manager",
1536
  { "Focus Manager",
1457
    NS_FOCUSMANAGER_CID,
1537
    NS_FOCUSMANAGER_CID,
1458
    "@mozilla.org/focus-manager;1",
1538
    "@mozilla.org/focus-manager;1",
1459
    CreateFocusManager },
1539
    CreateFocusManager },
1460
};
1540
};
1461
1541

Return to bug 493857