##// END OF EJS Templates
Merge remote-tracking branch 'upstream/master'
Nathan Heijermans -
r19175:e2ea8a76 merge
parent child Browse files
Show More
@@ -0,0 +1,4 b''
1 # URI for the CSP Report. Included here to prevent a cyclic dependency.
2 # csp_report_uri is needed both by the BaseHandler (for setting the report-uri)
3 # and by the CSPReportHandler (which depends on the BaseHandler).
4 csp_report_uri = r"/api/security/csp-report"
@@ -0,0 +1,23 b''
1 """Tornado handlers for security logging."""
2
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5
6 from tornado import gen, web
7
8 from ...base.handlers import IPythonHandler, json_errors
9 from . import csp_report_uri
10
11 class CSPReportHandler(IPythonHandler):
12 '''Accepts a content security policy violation report'''
13 @web.authenticated
14 @json_errors
15 def post(self):
16 '''Log a content security policy violation report'''
17 csp_report = self.get_json_body()
18 self.log.warn("Content security violation: %s",
19 self.request.body.decode('utf8', 'replace'))
20
21 default_handlers = [
22 (csp_report_uri, CSPReportHandler)
23 ]
@@ -6,6 +6,7 b''
6
6
7 from __future__ import print_function
7 from __future__ import print_function
8
8
9 import json
9 import logging
10 import logging
10 import os
11 import os
11 import re
12 import re
@@ -535,8 +536,17 b' class Application(SingletonConfigurable):'
535 def load_config_file(self, filename, path=None):
536 def load_config_file(self, filename, path=None):
536 """Load config files by filename and path."""
537 """Load config files by filename and path."""
537 filename, ext = os.path.splitext(filename)
538 filename, ext = os.path.splitext(filename)
539 loaded = []
538 for config in self._load_config_files(filename, path=path, log=self.log):
540 for config in self._load_config_files(filename, path=path, log=self.log):
541 loaded.append(config)
539 self.update_config(config)
542 self.update_config(config)
543 if len(loaded) > 1:
544 collisions = loaded[0].collisions(loaded[1])
545 if collisions:
546 self.log.warn("Collisions detected in {0}.py and {0}.json config files."
547 " {0}.json has higher priority: {1}".format(
548 filename, json.dumps(collisions, indent=2),
549 ))
540
550
541
551
542 def generate_config_file(self):
552 def generate_config_file(self):
@@ -193,7 +193,27 b' class Config(dict):'
193 to_update[k] = copy.deepcopy(v)
193 to_update[k] = copy.deepcopy(v)
194
194
195 self.update(to_update)
195 self.update(to_update)
196
196
197 def collisions(self, other):
198 """Check for collisions between two config objects.
199
200 Returns a dict of the form {"Class": {"trait": "collision message"}}`,
201 indicating which values have been ignored.
202
203 An empty dict indicates no collisions.
204 """
205 collisions = {}
206 for section in self:
207 if section not in other:
208 continue
209 mine = self[section]
210 theirs = other[section]
211 for key in mine:
212 if key in theirs and mine[key] != theirs[key]:
213 collisions.setdefault(section, {})
214 collisions[section][key] = "%r ignored, using %r" % (mine[key], theirs[key])
215 return collisions
216
197 def __contains__(self, key):
217 def __contains__(self, key):
198 # allow nested contains of the form `"Section.key" in config`
218 # allow nested contains of the form `"Section.key" in config`
199 if '.' in key:
219 if '.' in key:
@@ -565,7 +585,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
565
585
566
586
567 def _decode_argv(self, argv, enc=None):
587 def _decode_argv(self, argv, enc=None):
568 """decode argv if bytes, using stin.encoding, falling back on default enc"""
588 """decode argv if bytes, using stdin.encoding, falling back on default enc"""
569 uargv = []
589 uargv = []
570 if enc is None:
590 if enc is None:
571 enc = DEFAULT_ENCODING
591 enc = DEFAULT_ENCODING
@@ -1,28 +1,12 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """Tests for IPython.config.loader"""
3 Tests for IPython.config.loader
4
5 Authors:
6
7 * Brian Granger
8 * Fernando Perez (design help)
9 """
10
3
11 #-----------------------------------------------------------------------------
4 # Copyright (c) IPython Development Team.
12 # Copyright (C) 2008 The IPython Development Team
5 # Distributed under the terms of the Modified BSD License.
13 #
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
17
18 #-----------------------------------------------------------------------------
19 # Imports
20 #-----------------------------------------------------------------------------
21
6
22 import os
7 import os
23 import pickle
8 import pickle
24 import sys
9 import sys
25 import json
26
10
27 from tempfile import mkstemp
11 from tempfile import mkstemp
28 from unittest import TestCase
12 from unittest import TestCase
@@ -43,10 +27,6 b' from IPython.config.loader import ('
43 ConfigError,
27 ConfigError,
44 )
28 )
45
29
46 #-----------------------------------------------------------------------------
47 # Actual tests
48 #-----------------------------------------------------------------------------
49
50
30
51 pyfile = """
31 pyfile = """
52 c = get_config()
32 c = get_config()
@@ -117,6 +97,34 b' class TestFileCL(TestCase):'
117 cl = JSONFileConfigLoader(fname, log=log)
97 cl = JSONFileConfigLoader(fname, log=log)
118 config = cl.load_config()
98 config = cl.load_config()
119 self._check_conf(config)
99 self._check_conf(config)
100
101 def test_collision(self):
102 a = Config()
103 b = Config()
104 self.assertEqual(a.collisions(b), {})
105 a.A.trait1 = 1
106 b.A.trait2 = 2
107 self.assertEqual(a.collisions(b), {})
108 b.A.trait1 = 1
109 self.assertEqual(a.collisions(b), {})
110 b.A.trait1 = 0
111 self.assertEqual(a.collisions(b), {
112 'A': {
113 'trait1': "1 ignored, using 0",
114 }
115 })
116 self.assertEqual(b.collisions(a), {
117 'A': {
118 'trait1': "0 ignored, using 1",
119 }
120 })
121 a.A.trait2 = 3
122 self.assertEqual(b.collisions(a), {
123 'A': {
124 'trait1': "0 ignored, using 1",
125 'trait2': "2 ignored, using 3",
126 }
127 })
120
128
121 def test_v2raise(self):
129 def test_v2raise(self):
122 fd, fname = mkstemp('.json')
130 fd, fname = mkstemp('.json')
@@ -32,6 +32,8 b' from IPython.utils.path import filefind'
32 from IPython.utils.py3compat import string_types
32 from IPython.utils.py3compat import string_types
33 from IPython.html.utils import is_hidden, url_path_join, url_escape
33 from IPython.html.utils import is_hidden, url_path_join, url_escape
34
34
35 from IPython.html.services.security import csp_report_uri
36
35 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
36 # Top-level handlers
38 # Top-level handlers
37 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
@@ -45,17 +47,22 b' class AuthenticatedHandler(web.RequestHandler):'
45 def set_default_headers(self):
47 def set_default_headers(self):
46 headers = self.settings.get('headers', {})
48 headers = self.settings.get('headers', {})
47
49
48 if "X-Frame-Options" not in headers:
50 if "Content-Security-Policy" not in headers:
49 headers["X-Frame-Options"] = "SAMEORIGIN"
51 headers["Content-Security-Policy"] = (
52 "frame-ancestors 'self'; "
53 # Make sure the report-uri is relative to the base_url
54 "report-uri " + url_path_join(self.base_url, csp_report_uri) + ";"
55 )
50
56
57 # Allow for overriding headers
51 for header_name,value in headers.items() :
58 for header_name,value in headers.items() :
52 try:
59 try:
53 self.set_header(header_name, value)
60 self.set_header(header_name, value)
54 except Exception:
61 except Exception as e:
55 # tornado raise Exception (not a subclass)
62 # tornado raise Exception (not a subclass)
56 # if method is unsupported (websocket and Access-Control-Allow-Origin
63 # if method is unsupported (websocket and Access-Control-Allow-Origin
57 # for example, so just ignore)
64 # for example, so just ignore)
58 pass
65 self.log.debug(e)
59
66
60 def clear_login_cookie(self):
67 def clear_login_cookie(self):
61 self.clear_cookie(self.cookie_name)
68 self.clear_cookie(self.cookie_name)
@@ -225,7 +225,7 b' class NotebookWebApplication(web.Application):'
225 handlers.extend(load_handlers('services.sessions.handlers'))
225 handlers.extend(load_handlers('services.sessions.handlers'))
226 handlers.extend(load_handlers('services.nbconvert.handlers'))
226 handlers.extend(load_handlers('services.nbconvert.handlers'))
227 handlers.extend(load_handlers('services.kernelspecs.handlers'))
227 handlers.extend(load_handlers('services.kernelspecs.handlers'))
228
228 handlers.extend(load_handlers('services.security.handlers'))
229 handlers.append(
229 handlers.append(
230 (r"/nbextensions/(.*)", FileFindHandler, {
230 (r"/nbextensions/(.*)", FileFindHandler, {
231 'path': settings['nbextensions_path'],
231 'path': settings['nbextensions_path'],
@@ -0,0 +1,1 b''
1 from .manager import ConfigManager
@@ -65,7 +65,10 b' class KernelAPITest(NotebookTestBase):'
65 self.assertEqual(r.status_code, 201)
65 self.assertEqual(r.status_code, 201)
66 self.assertIsInstance(kern1, dict)
66 self.assertIsInstance(kern1, dict)
67
67
68 self.assertEqual(r.headers['x-frame-options'], "SAMEORIGIN")
68 self.assertEqual(r.headers['Content-Security-Policy'], (
69 "frame-ancestors 'self'; "
70 "report-uri /api/security/csp-report;"
71 ))
69
72
70 def test_main_kernel_handler(self):
73 def test_main_kernel_handler(self):
71 # POST request
74 # POST request
@@ -75,7 +78,10 b' class KernelAPITest(NotebookTestBase):'
75 self.assertEqual(r.status_code, 201)
78 self.assertEqual(r.status_code, 201)
76 self.assertIsInstance(kern1, dict)
79 self.assertIsInstance(kern1, dict)
77
80
78 self.assertEqual(r.headers['x-frame-options'], "SAMEORIGIN")
81 self.assertEqual(r.headers['Content-Security-Policy'], (
82 "frame-ancestors 'self'; "
83 "report-uri /api/security/csp-report;"
84 ))
79
85
80 # GET request
86 # GET request
81 r = self.kern_api.list()
87 r = self.kern_api.list()
@@ -286,16 +286,16 b' define(['
286 });
286 });
287 };
287 };
288
288
289 /**
290 * POST /api/kernels/[:kernel_id]/restart
291 *
292 * Restart the kernel.
293 *
294 * @function interrupt
295 * @param {function} [success] - function executed on ajax success
296 * @param {function} [error] - functon executed on ajax error
297 */
298 Kernel.prototype.restart = function (success, error) {
289 Kernel.prototype.restart = function (success, error) {
290 /**
291 * POST /api/kernels/[:kernel_id]/restart
292 *
293 * Restart the kernel.
294 *
295 * @function interrupt
296 * @param {function} [success] - function executed on ajax success
297 * @param {function} [error] - functon executed on ajax error
298 */
299 this.events.trigger('kernel_restarting.Kernel', {kernel: this});
299 this.events.trigger('kernel_restarting.Kernel', {kernel: this});
300 this.stop_channels();
300 this.stop_channels();
301
301
@@ -327,14 +327,14 b' define(['
327 });
327 });
328 };
328 };
329
329
330 /**
331 * Reconnect to a disconnected kernel. This is not actually a
332 * standard HTTP request, but useful function nonetheless for
333 * reconnecting to the kernel if the connection is somehow lost.
334 *
335 * @function reconnect
336 */
337 Kernel.prototype.reconnect = function () {
330 Kernel.prototype.reconnect = function () {
331 /**
332 * Reconnect to a disconnected kernel. This is not actually a
333 * standard HTTP request, but useful function nonetheless for
334 * reconnecting to the kernel if the connection is somehow lost.
335 *
336 * @function reconnect
337 */
338 if (this.is_connected()) {
338 if (this.is_connected()) {
339 return;
339 return;
340 }
340 }
@@ -346,15 +346,15 b' define(['
346 this.start_channels();
346 this.start_channels();
347 };
347 };
348
348
349 /**
350 * Handle a successful AJAX request by updating the kernel id and
351 * name from the response, and then optionally calling a provided
352 * callback.
353 *
354 * @function _on_success
355 * @param {function} success - callback
356 */
357 Kernel.prototype._on_success = function (success) {
349 Kernel.prototype._on_success = function (success) {
350 /**
351 * Handle a successful AJAX request by updating the kernel id and
352 * name from the response, and then optionally calling a provided
353 * callback.
354 *
355 * @function _on_success
356 * @param {function} success - callback
357 */
358 var that = this;
358 var that = this;
359 return function (data, status, xhr) {
359 return function (data, status, xhr) {
360 if (data) {
360 if (data) {
@@ -368,14 +368,14 b' define(['
368 };
368 };
369 };
369 };
370
370
371 /**
372 * Handle a failed AJAX request by logging the error message, and
373 * then optionally calling a provided callback.
374 *
375 * @function _on_error
376 * @param {function} error - callback
377 */
378 Kernel.prototype._on_error = function (error) {
371 Kernel.prototype._on_error = function (error) {
372 /**
373 * Handle a failed AJAX request by logging the error message, and
374 * then optionally calling a provided callback.
375 *
376 * @function _on_error
377 * @param {function} error - callback
378 */
379 return function (xhr, status, err) {
379 return function (xhr, status, err) {
380 utils.log_ajax_error(xhr, status, err);
380 utils.log_ajax_error(xhr, status, err);
381 if (error) {
381 if (error) {
@@ -384,27 +384,27 b' define(['
384 };
384 };
385 };
385 };
386
386
387 /**
388 * Perform necessary tasks once the kernel has been started,
389 * including actually connecting to the kernel.
390 *
391 * @function _kernel_created
392 * @param {Object} data - information about the kernel including id
393 */
394 Kernel.prototype._kernel_created = function (data) {
387 Kernel.prototype._kernel_created = function (data) {
388 /**
389 * Perform necessary tasks once the kernel has been started,
390 * including actually connecting to the kernel.
391 *
392 * @function _kernel_created
393 * @param {Object} data - information about the kernel including id
394 */
395 this.id = data.id;
395 this.id = data.id;
396 this.kernel_url = utils.url_join_encode(this.kernel_service_url, this.id);
396 this.kernel_url = utils.url_join_encode(this.kernel_service_url, this.id);
397 this.start_channels();
397 this.start_channels();
398 };
398 };
399
399
400 /**
401 * Perform necessary tasks once the connection to the kernel has
402 * been established. This includes requesting information about
403 * the kernel.
404 *
405 * @function _kernel_connected
406 */
407 Kernel.prototype._kernel_connected = function () {
400 Kernel.prototype._kernel_connected = function () {
401 /**
402 * Perform necessary tasks once the connection to the kernel has
403 * been established. This includes requesting information about
404 * the kernel.
405 *
406 * @function _kernel_connected
407 */
408 this.events.trigger('kernel_connected.Kernel', {kernel: this});
408 this.events.trigger('kernel_connected.Kernel', {kernel: this});
409 this.events.trigger('kernel_starting.Kernel', {kernel: this});
409 this.events.trigger('kernel_starting.Kernel', {kernel: this});
410 // get kernel info so we know what state the kernel is in
410 // get kernel info so we know what state the kernel is in
@@ -415,24 +415,24 b' define(['
415 });
415 });
416 };
416 };
417
417
418 /**
419 * Perform necessary tasks after the kernel has died. This closing
420 * communication channels to the kernel if they are still somehow
421 * open.
422 *
423 * @function _kernel_dead
424 */
425 Kernel.prototype._kernel_dead = function () {
418 Kernel.prototype._kernel_dead = function () {
419 /**
420 * Perform necessary tasks after the kernel has died. This closing
421 * communication channels to the kernel if they are still somehow
422 * open.
423 *
424 * @function _kernel_dead
425 */
426 this.stop_channels();
426 this.stop_channels();
427 };
427 };
428
428
429 /**
430 * Start the `shell`and `iopub` channels.
431 * Will stop and restart them if they already exist.
432 *
433 * @function start_channels
434 */
435 Kernel.prototype.start_channels = function () {
429 Kernel.prototype.start_channels = function () {
430 /**
431 * Start the `shell`and `iopub` channels.
432 * Will stop and restart them if they already exist.
433 *
434 * @function start_channels
435 */
436 var that = this;
436 var that = this;
437 this.stop_channels();
437 this.stop_channels();
438 var ws_host_url = this.ws_url + this.kernel_url;
438 var ws_host_url = this.ws_url + this.kernel_url;
@@ -506,29 +506,29 b' define(['
506 this.channels.stdin.onmessage = $.proxy(this._handle_input_request, this);
506 this.channels.stdin.onmessage = $.proxy(this._handle_input_request, this);
507 };
507 };
508
508
509 /**
510 * Handle a websocket entering the open state,
511 * signaling that the kernel is connected when all channels are open.
512 *
513 * @function _ws_opened
514 */
515 Kernel.prototype._ws_opened = function (evt) {
509 Kernel.prototype._ws_opened = function (evt) {
510 /**
511 * Handle a websocket entering the open state,
512 * signaling that the kernel is connected when all channels are open.
513 *
514 * @function _ws_opened
515 */
516 if (this.is_connected()) {
516 if (this.is_connected()) {
517 // all events ready, trigger started event.
517 // all events ready, trigger started event.
518 this._kernel_connected();
518 this._kernel_connected();
519 }
519 }
520 };
520 };
521
521
522 /**
523 * Handle a websocket entering the closed state. This closes the
524 * other communication channels if they are open. If the websocket
525 * was not closed due to an error, try to reconnect to the kernel.
526 *
527 * @function _ws_closed
528 * @param {string} ws_url - the websocket url
529 * @param {bool} error - whether the connection was closed due to an error
530 */
531 Kernel.prototype._ws_closed = function(ws_url, error) {
522 Kernel.prototype._ws_closed = function(ws_url, error) {
523 /**
524 * Handle a websocket entering the closed state. This closes the
525 * other communication channels if they are open. If the websocket
526 * was not closed due to an error, try to reconnect to the kernel.
527 *
528 * @function _ws_closed
529 * @param {string} ws_url - the websocket url
530 * @param {bool} error - whether the connection was closed due to an error
531 */
532 this.stop_channels();
532 this.stop_channels();
533
533
534 this.events.trigger('kernel_disconnected.Kernel', {kernel: this});
534 this.events.trigger('kernel_disconnected.Kernel', {kernel: this});
@@ -555,13 +555,13 b' define(['
555 }
555 }
556 };
556 };
557
557
558 /**
559 * Close the websocket channels. After successful close, the value
560 * in `this.channels[channel_name]` will be null.
561 *
562 * @function stop_channels
563 */
564 Kernel.prototype.stop_channels = function () {
558 Kernel.prototype.stop_channels = function () {
559 /**
560 * Close the websocket channels. After successful close, the value
561 * in `this.channels[channel_name]` will be null.
562 *
563 * @function stop_channels
564 */
565 var that = this;
565 var that = this;
566 var close = function (c) {
566 var close = function (c) {
567 return function () {
567 return function () {
@@ -582,15 +582,15 b' define(['
582 }
582 }
583 };
583 };
584
584
585 /**
586 * Check whether there is a connection to the kernel. This
587 * function only returns true if all channel objects have been
588 * created and have a state of WebSocket.OPEN.
589 *
590 * @function is_connected
591 * @returns {bool} - whether there is a connection
592 */
593 Kernel.prototype.is_connected = function () {
585 Kernel.prototype.is_connected = function () {
586 /**
587 * Check whether there is a connection to the kernel. This
588 * function only returns true if all channel objects have been
589 * created and have a state of WebSocket.OPEN.
590 *
591 * @function is_connected
592 * @returns {bool} - whether there is a connection
593 */
594 for (var c in this.channels) {
594 for (var c in this.channels) {
595 // if any channel is not ready, then we're not connected
595 // if any channel is not ready, then we're not connected
596 if (this.channels[c] === null) {
596 if (this.channels[c] === null) {
@@ -603,15 +603,15 b' define(['
603 return true;
603 return true;
604 };
604 };
605
605
606 /**
607 * Check whether the connection to the kernel has been completely
608 * severed. This function only returns true if all channel objects
609 * are null.
610 *
611 * @function is_fully_disconnected
612 * @returns {bool} - whether the kernel is fully disconnected
613 */
614 Kernel.prototype.is_fully_disconnected = function () {
606 Kernel.prototype.is_fully_disconnected = function () {
607 /**
608 * Check whether the connection to the kernel has been completely
609 * severed. This function only returns true if all channel objects
610 * are null.
611 *
612 * @function is_fully_disconnected
613 * @returns {bool} - whether the kernel is fully disconnected
614 */
615 for (var c in this.channels) {
615 for (var c in this.channels) {
616 if (this.channels[c] === null) {
616 if (this.channels[c] === null) {
617 return true;
617 return true;
@@ -620,12 +620,12 b' define(['
620 return false;
620 return false;
621 };
621 };
622
622
623 /**
624 * Send a message on the Kernel's shell channel
625 *
626 * @function send_shell_message
627 */
628 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata, buffers) {
623 Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata, buffers) {
624 /**
625 * Send a message on the Kernel's shell channel
626 *
627 * @function send_shell_message
628 */
629 if (!this.is_connected()) {
629 if (!this.is_connected()) {
630 throw new Error("kernel is not connected");
630 throw new Error("kernel is not connected");
631 }
631 }
@@ -635,17 +635,17 b' define(['
635 return msg.header.msg_id;
635 return msg.header.msg_id;
636 };
636 };
637
637
638 /**
639 * Get kernel info
640 *
641 * @function kernel_info
642 * @param callback {function}
643 *
644 * When calling this method, pass a callback function that expects one argument.
645 * The callback will be passed the complete `kernel_info_reply` message documented
646 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info)
647 */
648 Kernel.prototype.kernel_info = function (callback) {
638 Kernel.prototype.kernel_info = function (callback) {
639 /**
640 * Get kernel info
641 *
642 * @function kernel_info
643 * @param callback {function}
644 *
645 * When calling this method, pass a callback function that expects one argument.
646 * The callback will be passed the complete `kernel_info_reply` message documented
647 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info)
648 */
649 var callbacks;
649 var callbacks;
650 if (callback) {
650 if (callback) {
651 callbacks = { shell : { reply : callback } };
651 callbacks = { shell : { reply : callback } };
@@ -653,19 +653,19 b' define(['
653 return this.send_shell_message("kernel_info_request", {}, callbacks);
653 return this.send_shell_message("kernel_info_request", {}, callbacks);
654 };
654 };
655
655
656 /**
657 * Get info on an object
658 *
659 * When calling this method, pass a callback function that expects one argument.
660 * The callback will be passed the complete `inspect_reply` message documented
661 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
662 *
663 * @function inspect
664 * @param code {string}
665 * @param cursor_pos {integer}
666 * @param callback {function}
667 */
668 Kernel.prototype.inspect = function (code, cursor_pos, callback) {
656 Kernel.prototype.inspect = function (code, cursor_pos, callback) {
657 /**
658 * Get info on an object
659 *
660 * When calling this method, pass a callback function that expects one argument.
661 * The callback will be passed the complete `inspect_reply` message documented
662 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
663 *
664 * @function inspect
665 * @param code {string}
666 * @param cursor_pos {integer}
667 * @param callback {function}
668 */
669 var callbacks;
669 var callbacks;
670 if (callback) {
670 if (callback) {
671 callbacks = { shell : { reply : callback } };
671 callbacks = { shell : { reply : callback } };
@@ -679,56 +679,56 b' define(['
679 return this.send_shell_message("inspect_request", content, callbacks);
679 return this.send_shell_message("inspect_request", content, callbacks);
680 };
680 };
681
681
682 /**
683 * Execute given code into kernel, and pass result to callback.
684 *
685 * @async
686 * @function execute
687 * @param {string} code
688 * @param [callbacks] {Object} With the following keys (all optional)
689 * @param callbacks.shell.reply {function}
690 * @param callbacks.shell.payload.[payload_name] {function}
691 * @param callbacks.iopub.output {function}
692 * @param callbacks.iopub.clear_output {function}
693 * @param callbacks.input {function}
694 * @param {object} [options]
695 * @param [options.silent=false] {Boolean}
696 * @param [options.user_expressions=empty_dict] {Dict}
697 * @param [options.allow_stdin=false] {Boolean} true|false
698 *
699 * @example
700 *
701 * The options object should contain the options for the execute
702 * call. Its default values are:
703 *
704 * options = {
705 * silent : true,
706 * user_expressions : {},
707 * allow_stdin : false
708 * }
709 *
710 * When calling this method pass a callbacks structure of the
711 * form:
712 *
713 * callbacks = {
714 * shell : {
715 * reply : execute_reply_callback,
716 * payload : {
717 * set_next_input : set_next_input_callback,
718 * }
719 * },
720 * iopub : {
721 * output : output_callback,
722 * clear_output : clear_output_callback,
723 * },
724 * input : raw_input_callback
725 * }
726 *
727 * Each callback will be passed the entire message as a single
728 * arugment. Payload handlers will be passed the corresponding
729 * payload and the execute_reply message.
730 */
731 Kernel.prototype.execute = function (code, callbacks, options) {
682 Kernel.prototype.execute = function (code, callbacks, options) {
683 /**
684 * Execute given code into kernel, and pass result to callback.
685 *
686 * @async
687 * @function execute
688 * @param {string} code
689 * @param [callbacks] {Object} With the following keys (all optional)
690 * @param callbacks.shell.reply {function}
691 * @param callbacks.shell.payload.[payload_name] {function}
692 * @param callbacks.iopub.output {function}
693 * @param callbacks.iopub.clear_output {function}
694 * @param callbacks.input {function}
695 * @param {object} [options]
696 * @param [options.silent=false] {Boolean}
697 * @param [options.user_expressions=empty_dict] {Dict}
698 * @param [options.allow_stdin=false] {Boolean} true|false
699 *
700 * @example
701 *
702 * The options object should contain the options for the execute
703 * call. Its default values are:
704 *
705 * options = {
706 * silent : true,
707 * user_expressions : {},
708 * allow_stdin : false
709 * }
710 *
711 * When calling this method pass a callbacks structure of the
712 * form:
713 *
714 * callbacks = {
715 * shell : {
716 * reply : execute_reply_callback,
717 * payload : {
718 * set_next_input : set_next_input_callback,
719 * }
720 * },
721 * iopub : {
722 * output : output_callback,
723 * clear_output : clear_output_callback,
724 * },
725 * input : raw_input_callback
726 * }
727 *
728 * Each callback will be passed the entire message as a single
729 * arugment. Payload handlers will be passed the corresponding
730 * payload and the execute_reply message.
731 */
732 var content = {
732 var content = {
733 code : code,
733 code : code,
734 silent : true,
734 silent : true,
@@ -101,8 +101,7 b' define(['
101 var parameters = {model: model, options: options};
101 var parameters = {model: model, options: options};
102 var view = new ViewType(parameters);
102 var view = new ViewType(parameters);
103 view.listenTo(model, 'destroy', view.remove);
103 view.listenTo(model, 'destroy', view.remove);
104 view.render();
104 return Promise.resolve(view.render()).then(function() {return view;});
105 return view;
106 }).catch(utils.reject("Couldn't create a view for model id '" + String(model.id) + "'", true));
105 }).catch(utils.reject("Couldn't create a view for model id '" + String(model.id) + "'", true));
107 });
106 });
108 return model.state_change;
107 return model.state_change;
@@ -180,16 +180,42 b' Backwards incompatible changes'
180
180
181 .. DO NOT EDIT THIS LINE BEFORE RELEASE. INCOMPAT INSERTION POINT.
181 .. DO NOT EDIT THIS LINE BEFORE RELEASE. INCOMPAT INSERTION POINT.
182
182
183 IFrame embedding
183 Content Security Policy
184 ````````````````
184 ```````````````````````
185
185
186 The IPython Notebook and its APIs by default will only be allowed to be
186 The Content Security Policy is a web standard for adding a layer of security to
187 embedded in an iframe on the same origin.
187 detect and mitigate certain classes of attacks, including Cross Site Scripting
188 (XSS) and data injection attacks. This was introduced into the notebook to
189 ensure that the IPython Notebook and its APIs (by default) can only be embedded
190 in an iframe on the same origin.
188
191
189 To override this, set ``headers[X-Frame-Options]`` to one of
192 Override ``headers['Content-Security-Policy']`` within your notebook
193 configuration to extend for alternate domains and security settings.::
190
194
191 * DENY
195 c.NotebookApp.tornado_settings = {
192 * SAMEORIGIN
196 'headers': {
193 * ALLOW-FROM uri
197 'Content-Security-Policy': "frame-ancestors 'self'"
198 }
199 }
194
200
195 See `Mozilla's guide to X-Frame-Options <https://developer.mozilla.org/en-US/docs/Web/HTTP/X-Frame-Options>`_ for more examples.
201 Example policies::
202
203 Content-Security-Policy: default-src 'self' https://*.jupyter.org
204
205 Matches embeddings on any subdomain of jupyter.org, so long as they are served
206 over SSL.
207
208 There is a `report-uri <https://developer.mozilla.org/en-US/docs/Web/Security/CSP/CSP_policy_directives#report-uri>`_ endpoint available for logging CSP violations, located at
209 ``/api/security/csp-report``. To use it, set ``report-uri`` as part of the CSP::
210
211 c.NotebookApp.tornado_settings = {
212 'headers': {
213 'Content-Security-Policy': "frame-ancestors 'self'; report-uri /api/security/csp-report"
214 }
215 }
216
217 It simply provides the CSP report as a warning in IPython's logs. The default
218 CSP sets this report-uri relative to the ``base_url`` (not shown above).
219
220 For a more thorough and accurate guide on Content Security Policies, check out
221 `MDN's Using Content Security Policy <https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Using_Content_Security_Policy>`_ for more examples.
General Comments 0
You need to be logged in to leave comments. Login now