diff --git a/SECURITY.md b/SECURITY.md index 4668486..e9e4c49 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,10 +4,7 @@ **Do not open a public GitHub issue for security vulnerabilities.** -Report privately through either channel: - -- **GitHub Private Vulnerability Reporting** (preferred): [Report a vulnerability](https://github.com/ionq/ionq-core-python/security/advisories/new). The report is visible only to repository maintainers and people you invite to the advisory. -- **Email**: [security@ionq.co](mailto:security@ionq.co) with the subject line `[ionq-core-python]`. +Email [security@ionq.co](mailto:security@ionq.co) with the subject line `[ionq-core-python]`. Please include enough detail to reproduce the issue, and redact your API key from any logs or response payloads you share. @@ -25,7 +22,7 @@ When conducting security research consistent with this policy, we consider your - We will not bring a claim against you for circumvention of technical controls under relevant anti-circumvention laws (such as DMCA section 1201). - If a third party initiates legal action against you for activities conducted in good-faith compliance with this policy, we will take steps to make it known that your actions were authorized. -In return, we ask that you comply with all applicable laws, make reasonable efforts to avoid privacy violations, service disruption, and destruction of data, limit testing to your own account or accounts you control, and use the channels above to discuss vulnerabilities with us. +In return, we ask that you comply with all applicable laws, make reasonable efforts to avoid privacy violations, service disruption, and destruction of data, limit testing to your own account or accounts you control, and use the email above to discuss vulnerabilities with us. If you are unsure whether a planned activity is consistent with this policy, contact before proceeding. Safe harbor applies only to claims within IonQ's control; this policy does not bind independent third parties. diff --git a/tests/test_docs_consistency.py b/tests/test_docs_consistency.py index c22f25f..c75ae5b 100644 --- a/tests/test_docs_consistency.py +++ b/tests/test_docs_consistency.py @@ -9,23 +9,17 @@ import pytest from ionq_core import extensions, polling -from ionq_core._transport import DEFAULT_MAX_RETRIES, RETRYABLE_STATUS_CODES +from ionq_core._transport import DEFAULT_MAX_RETRIES from ionq_core.ionq_client import DEFAULT_BASE_URL, DEFAULT_TIMEOUT from ionq_core.polling import _BACKOFF_FACTOR, _MAX_INTERVAL from ionq_core.polling import _DEFAULT_TIMEOUT as _POLL_DEFAULT_TIMEOUT ROOT = Path(__file__).parent.parent -README = (ROOT / "README.md").read_text() PYPROJECT = tomllib.loads((ROOT / "pyproject.toml").read_text()) GITATTRIBUTES = (ROOT / ".gitattributes").read_text() CONTRIB = (ROOT / "CONTRIBUTING.md").read_text() GENERATED_WF = (ROOT / ".github" / "workflows" / "generated.yml").read_text() SPEC_DRIFT_WF = (ROOT / ".github" / "workflows" / "spec-drift.yml").read_text() -SESSION_PY = (ROOT / "ionq_core" / "session.py").read_text() - -PACKAGE_DESCRIPTION = "A client library for accessing IonQ Cloud Platform API" -EXAMPLE_BACKEND = "qpu.aria-1" -_BACKEND_PATTERN = re.compile(r'SessionManager\([^)]*?"([^"]+)"') def _normalize(path: str) -> str: @@ -55,15 +49,9 @@ def _pin(text: str, package: str) -> str: return m.group(1) -def test_retryable_status_codes_match_runtime(): - assert frozenset({429, 500, 502, 503, *range(520, 530)}) == RETRYABLE_STATUS_CODES - - @pytest.mark.parametrize( "needle", [ - *(str(c) for c in (429, 500, 502, 503)), - "520-529", f"default of {DEFAULT_MAX_RETRIES}", f"default of {int(DEFAULT_TIMEOUT.read)} seconds", ], @@ -86,11 +74,6 @@ def test_polling_docstring_pins(fn, needle): assert needle in (fn.__doc__ or ""), f"{needle!r} missing from {fn.__name__}" -def test_session_example_backend_consistent(): - backends = set(_BACKEND_PATTERN.findall(SESSION_PY)) - assert backends == {EXAMPLE_BACKEND}, f"divergent backends in session.py: {backends}" - - def test_pyproject_floor_matches_ci_matrix(): assert _python_floor() == min(_ci_python_versions()) @@ -118,20 +101,6 @@ def test_classifiers_match_ci_matrix(): assert sorted(_ci_python_versions()) == classifiers, f"matrix={_ci_python_versions()} classifiers={classifiers}" -def test_pyproject_description_canonical(): - assert PYPROJECT["project"]["description"] == PACKAGE_DESCRIPTION - - -def test_init_module_docstring_canonical(): - import ionq_core - - assert (ionq_core.__doc__ or "").strip() == PACKAGE_DESCRIPTION - - -def test_readme_tagline_canonical(): - assert PACKAGE_DESCRIPTION in README - - def test_ruff_excludes_match_coverage_omits(): ruff = {_normalize(p) for p in PYPROJECT["tool"]["ruff"]["extend-exclude"]} coverage = {_normalize(p) for p in PYPROJECT["tool"]["coverage"]["run"]["omit"]} @@ -139,8 +108,7 @@ def test_ruff_excludes_match_coverage_omits(): def test_gitattributes_covers_ruff_paths_plus_init(): - # __init__.py is generated (template-driven) but kept in scope for ruff/coverage - # because the template is hand-maintained. .gitattributes still marks it generated. + # __init__.py: hand-edited template, generated output; in ruff/coverage scope, marked linguist-generated. gitattr = { _normalize(line.split()[0]) for line in GITATTRIBUTES.splitlines() @@ -159,24 +127,22 @@ def test_oas_patch_versions_match(): def test_spec_path_matches_default_base_url(): - # Pinning to DEFAULT_BASE_URL means a v0.4 -> v0.5 bump fails this test until - # CONTRIBUTING and spec-drift.yml are updated too. Otherwise the drift workflow - # would silently keep curl'ing the stale endpoint. + # Without this, a DEFAULT_BASE_URL bump leaves spec-drift.yml curling the stale endpoint. spec_path = f"{urlparse(DEFAULT_BASE_URL).path}/api-docs" assert spec_path in CONTRIB assert spec_path in SPEC_DRIFT_WF -def test_default_base_url_matches_spec_servers(): +def test_spec_servers_path_in_docs(): + # Catches a stale openapi.json: code/docs bumped without regen, or fetched from the wrong version. spec = json.loads((ROOT / "openapi.json").read_text()) - assert spec["servers"][0]["url"] == DEFAULT_BASE_URL + spec_path = urlparse(spec["servers"][0]["url"]).path + assert f"{spec_path}/api-docs" in CONTRIB + assert f"{spec_path}/api-docs" in SPEC_DRIFT_WF def test_single_spdx_year_across_package(): - """Generated files get the year injected by the openapi-python-client post-hook; - hand-written files have a static year. After a new-year regen, both sets must - be bumped together. - """ + """Generated files get the year via post-hook; hand-written files must be bumped to match at year boundaries.""" years = set() for py in (ROOT / "ionq_core").rglob("*.py"): m = re.match(r"# SPDX-FileCopyrightText: (\d{4}) IonQ, Inc\.", py.read_text())