##// END OF EJS Templates
restore ability to sign v3 notebooks
MinRK -
Show More
@@ -309,15 +309,12 b' class ContentsManager(LoggingConfigurable):'
309 Parameters
309 Parameters
310 ----------
310 ----------
311 nb : dict
311 nb : dict
312 The notebook object (in current nbformat)
312 The notebook dict
313 name : string
313 name : string
314 The filename of the notebook (for logging)
314 The filename of the notebook (for logging)
315 path : string
315 path : string
316 The notebook's directory (for logging)
316 The notebook's directory (for logging)
317 """
317 """
318 # can't sign old notebooks
319 if nb['nbformat'] != 4:
320 return
321 if self.notary.check_cells(nb):
318 if self.notary.check_cells(nb):
322 self.notary.sign(nb)
319 self.notary.sign(nb)
323 else:
320 else:
@@ -46,6 +46,20 b' def yield_everything(obj):'
46 else:
46 else:
47 yield unicode_type(obj).encode('utf8')
47 yield unicode_type(obj).encode('utf8')
48
48
49 def yield_code_cells(nb):
50 """Iterator that yields all cells in a notebook
51
52 nbformat version independent
53 """
54 if nb.nbformat >= 4:
55 for cell in nb['cells']:
56 if cell['cell_type'] == 'code':
57 yield cell
58 elif nb.nbformat == 3:
59 for ws in nb['worksheets']:
60 for cell in ws['cells']:
61 if cell['cell_type'] == 'code':
62 yield cell
49
63
50 @contextmanager
64 @contextmanager
51 def signature_removed(nb):
65 def signature_removed(nb):
@@ -152,6 +166,8 b' class NotebookNotary(LoggingConfigurable):'
152 - the requested scheme is available from hashlib
166 - the requested scheme is available from hashlib
153 - the computed hash from notebook_signature matches the stored hash
167 - the computed hash from notebook_signature matches the stored hash
154 """
168 """
169 if nb.nbformat < 3:
170 return False
155 stored_signature = nb['metadata'].get('signature', None)
171 stored_signature = nb['metadata'].get('signature', None)
156 if not stored_signature \
172 if not stored_signature \
157 or not isinstance(stored_signature, string_types) \
173 or not isinstance(stored_signature, string_types) \
@@ -170,6 +186,8 b' class NotebookNotary(LoggingConfigurable):'
170
186
171 e.g. 'sha256:deadbeef123...'
187 e.g. 'sha256:deadbeef123...'
172 """
188 """
189 if nb.nbformat < 3:
190 return
173 signature = self.compute_signature(nb)
191 signature = self.compute_signature(nb)
174 nb['metadata']['signature'] = "%s:%s" % (self.algorithm, signature)
192 nb['metadata']['signature'] = "%s:%s" % (self.algorithm, signature)
175
193
@@ -181,11 +199,13 b' class NotebookNotary(LoggingConfigurable):'
181
199
182 This function is the inverse of check_cells
200 This function is the inverse of check_cells
183 """
201 """
184 for cell in nb['cells']:
202 if nb.nbformat < 3:
185 if cell['cell_type'] == 'code':
203 return
186 cell['metadata']['trusted'] = trusted
204
205 for cell in yield_code_cells(nb):
206 cell['metadata']['trusted'] = trusted
187
207
188 def _check_cell(self, cell):
208 def _check_cell(self, cell, nbformat_version):
189 """Do we trust an individual cell?
209 """Do we trust an individual cell?
190
210
191 Return True if:
211 Return True if:
@@ -201,16 +221,21 b' class NotebookNotary(LoggingConfigurable):'
201 return True
221 return True
202
222
203 # explicitly safe output
223 # explicitly safe output
204 safe = {
224 if nbformat_version >= 4:
205 'text/plain', 'image/png', 'image/jpeg',
225 safe = {'text/plain', 'image/png', 'image/jpeg'}
206 }
226 unsafe_output_types = ['execute_result', 'display_data']
227 safe_keys = {"output_type", "execution_count", "metadata"}
228 else: # v3
229 safe = {'text', 'png', 'jpeg'}
230 unsafe_output_types = ['pyout', 'display_data']
231 safe_keys = {"output_type", "prompt_number", "metadata"}
207
232
208 for output in cell['outputs']:
233 for output in cell['outputs']:
209 output_type = output['output_type']
234 output_type = output['output_type']
210 if output_type in {'execute_result', 'display_data'}:
235 if output_type in unsafe_output_types:
211 # if there are any data keys not in the safe whitelist
236 # if there are any data keys not in the safe whitelist
212 output_keys = set(output).difference({"output_type", "execution_count", "metadata"})
237 output_keys = set(output)
213 if output_keys.difference(safe):
238 if output_keys.difference(safe_keys):
214 return False
239 return False
215
240
216 return True
241 return True
@@ -222,12 +247,12 b' class NotebookNotary(LoggingConfigurable):'
222
247
223 This function is the inverse of mark_cells.
248 This function is the inverse of mark_cells.
224 """
249 """
250 if nb.nbformat < 3:
251 return False
225 trusted = True
252 trusted = True
226 for cell in nb['cells']:
253 for cell in yield_code_cells(nb):
227 if cell['cell_type'] != 'code':
228 continue
229 # only distrust a cell if it actually has some output to distrust
254 # only distrust a cell if it actually has some output to distrust
230 if not self._check_cell(cell):
255 if not self._check_cell(cell, nb.nbformat):
231 trusted = False
256 trusted = False
232
257
233 return trusted
258 return trusted
@@ -18,6 +18,8 b' class TestNotary(TestsBase):'
18 )
18 )
19 with self.fopen(u'test3.ipynb', u'r') as f:
19 with self.fopen(u'test3.ipynb', u'r') as f:
20 self.nb = read(f, as_version=4)
20 self.nb = read(f, as_version=4)
21 with self.fopen(u'test3.ipynb', u'r') as f:
22 self.nb3 = read(f, as_version=3)
21
23
22 def test_algorithms(self):
24 def test_algorithms(self):
23 last_sig = ''
25 last_sig = ''
@@ -108,4 +110,41 b' class TestNotary(TestsBase):'
108 if cell.cell_type == 'code':
110 if cell.cell_type == 'code':
109 cell.outputs = []
111 cell.outputs = []
110 self.assertTrue(self.notary.check_cells(nb))
112 self.assertTrue(self.notary.check_cells(nb))
113
114 def test_mark_cells_untrusted_v3(self):
115 nb = self.nb3
116 cells = nb.worksheets[0].cells
117 self.notary.mark_cells(nb, False)
118 for cell in cells:
119 self.assertNotIn('trusted', cell)
120 if cell.cell_type == 'code':
121 self.assertIn('trusted', cell.metadata)
122 self.assertFalse(cell.metadata.trusted)
123 else:
124 self.assertNotIn('trusted', cell.metadata)
125
126 def test_mark_cells_trusted_v3(self):
127 nb = self.nb3
128 cells = nb.worksheets[0].cells
129 self.notary.mark_cells(nb, True)
130 for cell in cells:
131 self.assertNotIn('trusted', cell)
132 if cell.cell_type == 'code':
133 self.assertIn('trusted', cell.metadata)
134 self.assertTrue(cell.metadata.trusted)
135 else:
136 self.assertNotIn('trusted', cell.metadata)
137
138 def test_check_cells_v3(self):
139 nb = self.nb3
140 cells = nb.worksheets[0].cells
141 self.notary.mark_cells(nb, True)
142 self.assertTrue(self.notary.check_cells(nb))
143 for cell in cells:
144 self.assertNotIn('trusted', cell)
145 self.notary.mark_cells(nb, False)
146 self.assertFalse(self.notary.check_cells(nb))
147 for cell in cells:
148 self.assertNotIn('trusted', cell)
149
111
150
General Comments 0
You need to be logged in to leave comments. Login now