Skip to content

Commit dec10b4

Browse files
authored
Merge branch 'master' into mh-fix-379
2 parents 5389910 + ac26599 commit dec10b4

File tree

5 files changed

+82
-8
lines changed

5 files changed

+82
-8
lines changed

.github/workflows/pythonpackage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ jobs:
3838
- name: Install dependencies
3939
run: |
4040
python -m pip install --upgrade pip
41+
pip install -r requirements.txt
4142
python dev_tools/write-ci-requirements.py --relative-cirq-version=${{ matrix.cirq-version }} --all-extras
4243
pip install -r ci-requirements.txt
4344
pip install --no-deps -e .
@@ -52,7 +53,6 @@ jobs:
5253
5354
- name: Test with pytest
5455
run: |
55-
pip install pytest
5656
# RECIRQ_IMPORT_FAILSAFE: skip tests on unsupported Cirq configurations
5757
# EXPORT_OMP_NUM_THREADS: pyscf has poor openmp performance which slows down qcqmc tests.
5858
export OMP_NUM_THREADS=1

CONTRIBUTING.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,49 @@ Guides](https://google.github.io/styleguide/) in your code, particularly the
6969

7070
### Development and testing
7171

72-
Do your work and `git commit` your changes to your branch as needed.
72+
Before you begin developing ReCirq experiments and modules, we recommend you
73+
first create a virtual Python environment. You can use either Python's built-in
74+
[`venv`](https://docs.python.org/3/library/venv.html) module or another tool
75+
that you are comfortable with.
76+
77+
Then, in that virtual environment, install the ReCirq dependencies using `pip`:
78+
79+
```shell
80+
pip install -r requirements.txt
81+
```
82+
83+
You _may_ need to install additional requirements, depending on the ReCirq
84+
experiment you want to work on. These additional requirements will be in a
85+
file named `extra-requirements.txt` in the experiment's subdirectory under
86+
`recirq/`. For example, if you were working with `recirq/optimize`, you would
87+
need to install its extra dependencies like this:
88+
89+
```shell
90+
pip install -r recirq/optimize/extra-requirements.txt
91+
```
92+
93+
Finally, if you are going to edit any of the Jupyter notebooks, install the
94+
additional requirements needed to run the notebook format checks:
95+
96+
```shell
97+
pip install -r dev_tools/requirements/deps/tensorflow-docs.txt
98+
```
99+
100+
Once the environment is set up, you can do your work and `git commit` your
101+
changes to your branch as needed.
102+
103+
To test notebooks for proper formatting and other issues, run the following
104+
command:
105+
106+
```shell
107+
dev_tools/nbformat
108+
```
109+
110+
To test your code, run
111+
112+
```shell
113+
pytest recirq
114+
```
73115

74116
### Pull requests and code reviews
75117

recirq/documentation_utils.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
import inspect
16+
import io
1617
import os
1718
import re
1819
import tarfile
@@ -67,6 +68,14 @@ def fetch_guide_data_collection_data(base_dir=None):
6768

6869
print("Downloading guide/data_collection data.")
6970
stream = urllib.request.urlopen("https://ndownloader.figshare.com/files/25541666")
70-
71-
with tarfile.open(fileobj=stream, mode='r|xz') as tf:
72-
tf.extractall(path=base_dir)
71+
# Read into a BytesIO object to make it seekable
72+
stream_bytes = io.BytesIO(stream.read())
73+
74+
with tarfile.open(fileobj=stream_bytes, mode='r|xz') as tf:
75+
for member in tf.getmembers():
76+
# Ensure the path being extracted is safe.
77+
if not os.path.abspath(os.path.join(base_dir, member.name)).startswith(
78+
os.path.abspath(base_dir)
79+
):
80+
raise ValueError(f"Encountered untrusted path {member.name}")
81+
tf.extract(member, path=base_dir)

recirq/documentation_utils_test.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
import os
15+
import io
16+
import tarfile
17+
from unittest import mock
18+
19+
import pytest
1520

1621
import recirq
1722
from recirq.readout_scan.tasks import ReadoutScanTask
@@ -35,6 +40,23 @@ def test_display_markdown_docstring():
3540
"""
3641

3742

38-
def test_fetch_guide_data_collection_data(tmpdir):
39-
recirq.fetch_guide_data_collection_data(base_dir=tmpdir)
40-
assert os.path.exists(f'{tmpdir}/2020-02-tutorial')
43+
@mock.patch('urllib.request.urlopen')
44+
def test_fetch_guide_data_collection_data_traversal(mock_urlopen, tmpdir):
45+
# Create a malicious tarball in memory.
46+
malicious_tar_stream = io.BytesIO()
47+
with tarfile.open(fileobj=malicious_tar_stream, mode='w:xz') as tf:
48+
# Add a file that tries to write outside the target directory
49+
malicious_info = tarfile.TarInfo(name="../../tmp/pwned")
50+
tf.addfile(malicious_info, io.BytesIO(b"pwned"))
51+
malicious_tar_stream.seek(0)
52+
53+
# Read the stream into a BytesIO object so that the mock should return a
54+
# response object whose read() method returns the tarball content.
55+
mock_response = mock.Mock()
56+
mock_response.read.return_value = malicious_tar_stream.getvalue()
57+
mock_urlopen.return_value = mock_response
58+
59+
with pytest.raises(ValueError, match="Encountered untrusted path"):
60+
recirq.fetch_guide_data_collection_data(base_dir=tmpdir)
61+
62+
assert not os.path.exists('/tmp/pwned')

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ seaborn
66
sphinx
77
ipython
88
black
9+
pytest

0 commit comments

Comments
 (0)