diff --git a/.stats.yml b/.stats.yml index 86c13449517..5e87bf85241 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 1475 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-663ea50351c42f1c51f49f099c888745f6a06cf22b8fe46cf1fa9ae89fdfdd61.yml +configured_endpoints: 1483 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-f9dc65d353b377858f3d63d594de47559ed3a9d562295719690867315b8ec2fe.yml diff --git a/api.md b/api.md index 3a11184648f..a5cf1ff287a 100644 --- a/api.md +++ b/api.md @@ -6594,6 +6594,51 @@ Methods: - client.vectorize.indexes.metadata_index.list(index_name, \*, account_id) -> Optional[MetadataIndexListResponse] - client.vectorize.indexes.metadata_index.delete(index_name, \*, account_id, \*\*params) -> Optional[MetadataIndexDeleteResponse] +# URLScanner + +Types: + +```python +from cloudflare.types.url_scanner import URLScannerDomain, URLScannerTask +``` + +## Responses + +Types: + +```python +from cloudflare.types.url_scanner import ResponseGetResponse +``` + +Methods: + +- client.url_scanner.responses.get(response_id, \*, account_id) -> str + +## Scans + +Types: + +```python +from cloudflare.types.url_scanner import ( + ScanCreateResponse, + ScanListResponse, + ScanBulkCreateResponse, + ScanDOMResponse, + ScanGetResponse, + ScanHARResponse, +) +``` + +Methods: + +- client.url_scanner.scans.create(\*, account_id, \*\*params) -> str +- client.url_scanner.scans.list(\*, account_id, \*\*params) -> ScanListResponse +- client.url_scanner.scans.bulk_create(\*, account_id, \*\*params) -> ScanBulkCreateResponse +- client.url_scanner.scans.dom(scan_id, \*, account_id) -> str +- client.url_scanner.scans.get(scan_id, \*, account_id) -> ScanGetResponse +- client.url_scanner.scans.har(scan_id, \*, account_id) -> ScanHARResponse +- client.url_scanner.scans.screenshot(scan_id, \*, account_id, \*\*params) -> BinaryAPIResponse + # Radar ## AI diff --git a/src/cloudflare/_client.py b/src/cloudflare/_client.py index 8b18c73296e..f48b5ae2359 100644 --- a/src/cloudflare/_client.py +++ b/src/cloudflare/_client.py @@ -88,6 +88,7 @@ memberships, page_shield, rate_limits, + url_scanner, dns_firewall, healthchecks, security_txt, @@ -185,6 +186,7 @@ from .resources.botnet_feed.botnet_feed import BotnetFeedResource, AsyncBotnetFeedResource from .resources.diagnostics.diagnostics import DiagnosticsResource, AsyncDiagnosticsResource from .resources.page_shield.page_shield import PageShieldResource, AsyncPageShieldResource + from .resources.url_scanner.url_scanner import URLScannerResource, AsyncURLScannerResource from .resources.dns_firewall.dns_firewall import DNSFirewallResource, AsyncDNSFirewallResource from .resources.healthchecks.healthchecks import HealthchecksResource, AsyncHealthchecksResource from .resources.email_routing.email_routing import EmailRoutingResource, AsyncEmailRoutingResource @@ -704,6 +706,12 @@ def vectorize(self) -> VectorizeResource: return VectorizeResource(self) + @cached_property + def url_scanner(self) -> URLScannerResource: + from .resources.url_scanner import URLScannerResource + + return URLScannerResource(self) + @cached_property def radar(self) -> RadarResource: from .resources.radar import RadarResource @@ -1469,6 +1477,12 @@ def vectorize(self) -> AsyncVectorizeResource: return AsyncVectorizeResource(self) + @cached_property + def url_scanner(self) -> AsyncURLScannerResource: + from .resources.url_scanner import AsyncURLScannerResource + + return AsyncURLScannerResource(self) + @cached_property def radar(self) -> AsyncRadarResource: from .resources.radar import AsyncRadarResource @@ -2167,6 +2181,12 @@ def vectorize(self) -> vectorize.VectorizeResourceWithRawResponse: return VectorizeResourceWithRawResponse(self._client.vectorize) + @cached_property + def url_scanner(self) -> url_scanner.URLScannerResourceWithRawResponse: + from .resources.url_scanner import URLScannerResourceWithRawResponse + + return URLScannerResourceWithRawResponse(self._client.url_scanner) + @cached_property def radar(self) -> radar.RadarResourceWithRawResponse: from .resources.radar import RadarResourceWithRawResponse @@ -2686,6 +2706,12 @@ def vectorize(self) -> vectorize.AsyncVectorizeResourceWithRawResponse: return AsyncVectorizeResourceWithRawResponse(self._client.vectorize) + @cached_property + def url_scanner(self) -> url_scanner.AsyncURLScannerResourceWithRawResponse: + from .resources.url_scanner import AsyncURLScannerResourceWithRawResponse + + return AsyncURLScannerResourceWithRawResponse(self._client.url_scanner) + @cached_property def radar(self) -> radar.AsyncRadarResourceWithRawResponse: from .resources.radar import AsyncRadarResourceWithRawResponse @@ -3205,6 +3231,12 @@ def vectorize(self) -> vectorize.VectorizeResourceWithStreamingResponse: return VectorizeResourceWithStreamingResponse(self._client.vectorize) + @cached_property + def url_scanner(self) -> url_scanner.URLScannerResourceWithStreamingResponse: + from .resources.url_scanner import URLScannerResourceWithStreamingResponse + + return URLScannerResourceWithStreamingResponse(self._client.url_scanner) + @cached_property def radar(self) -> radar.RadarResourceWithStreamingResponse: from .resources.radar import RadarResourceWithStreamingResponse @@ -3728,6 +3760,12 @@ def vectorize(self) -> vectorize.AsyncVectorizeResourceWithStreamingResponse: return AsyncVectorizeResourceWithStreamingResponse(self._client.vectorize) + @cached_property + def url_scanner(self) -> url_scanner.AsyncURLScannerResourceWithStreamingResponse: + from .resources.url_scanner import AsyncURLScannerResourceWithStreamingResponse + + return AsyncURLScannerResourceWithStreamingResponse(self._client.url_scanner) + @cached_property def radar(self) -> radar.AsyncRadarResourceWithStreamingResponse: from .resources.radar import AsyncRadarResourceWithStreamingResponse diff --git a/src/cloudflare/resources/url_scanner/__init__.py b/src/cloudflare/resources/url_scanner/__init__.py new file mode 100644 index 00000000000..047e0b9adea --- /dev/null +++ b/src/cloudflare/resources/url_scanner/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .scans import ( + ScansResource, + AsyncScansResource, + ScansResourceWithRawResponse, + AsyncScansResourceWithRawResponse, + ScansResourceWithStreamingResponse, + AsyncScansResourceWithStreamingResponse, +) +from .responses import ( + ResponsesResource, + AsyncResponsesResource, + ResponsesResourceWithRawResponse, + AsyncResponsesResourceWithRawResponse, + ResponsesResourceWithStreamingResponse, + AsyncResponsesResourceWithStreamingResponse, +) +from .url_scanner import ( + URLScannerResource, + AsyncURLScannerResource, + URLScannerResourceWithRawResponse, + AsyncURLScannerResourceWithRawResponse, + URLScannerResourceWithStreamingResponse, + AsyncURLScannerResourceWithStreamingResponse, +) + +__all__ = [ + "ResponsesResource", + "AsyncResponsesResource", + "ResponsesResourceWithRawResponse", + "AsyncResponsesResourceWithRawResponse", + "ResponsesResourceWithStreamingResponse", + "AsyncResponsesResourceWithStreamingResponse", + "ScansResource", + "AsyncScansResource", + "ScansResourceWithRawResponse", + "AsyncScansResourceWithRawResponse", + "ScansResourceWithStreamingResponse", + "AsyncScansResourceWithStreamingResponse", + "URLScannerResource", + "AsyncURLScannerResource", + "URLScannerResourceWithRawResponse", + "AsyncURLScannerResourceWithRawResponse", + "URLScannerResourceWithStreamingResponse", + "AsyncURLScannerResourceWithStreamingResponse", +] diff --git a/src/cloudflare/resources/url_scanner/responses.py b/src/cloudflare/resources/url_scanner/responses.py new file mode 100644 index 00000000000..322e44344bc --- /dev/null +++ b/src/cloudflare/resources/url_scanner/responses.py @@ -0,0 +1,182 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options + +__all__ = ["ResponsesResource", "AsyncResponsesResource"] + + +class ResponsesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ResponsesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers + """ + return ResponsesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ResponsesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response + """ + return ResponsesResourceWithStreamingResponse(self) + + def get( + self, + response_id: str, + *, + account_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> str: + """Returns the raw response of the network request. + + If HTML, a plain text response + will be returned. + + Args: + account_id: Account ID. + + response_id: Response hash. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "text/plain", **(extra_headers or {})} + return self._get( + f"/accounts/{account_id}/urlscanner/v2/responses/{response_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + ) + + +class AsyncResponsesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncResponsesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers + """ + return AsyncResponsesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncResponsesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response + """ + return AsyncResponsesResourceWithStreamingResponse(self) + + async def get( + self, + response_id: str, + *, + account_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> str: + """Returns the raw response of the network request. + + If HTML, a plain text response + will be returned. + + Args: + account_id: Account ID. + + response_id: Response hash. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "text/plain", **(extra_headers or {})} + return await self._get( + f"/accounts/{account_id}/urlscanner/v2/responses/{response_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + ) + + +class ResponsesResourceWithRawResponse: + def __init__(self, responses: ResponsesResource) -> None: + self._responses = responses + + self.get = to_raw_response_wrapper( + responses.get, + ) + + +class AsyncResponsesResourceWithRawResponse: + def __init__(self, responses: AsyncResponsesResource) -> None: + self._responses = responses + + self.get = async_to_raw_response_wrapper( + responses.get, + ) + + +class ResponsesResourceWithStreamingResponse: + def __init__(self, responses: ResponsesResource) -> None: + self._responses = responses + + self.get = to_streamed_response_wrapper( + responses.get, + ) + + +class AsyncResponsesResourceWithStreamingResponse: + def __init__(self, responses: AsyncResponsesResource) -> None: + self._responses = responses + + self.get = async_to_streamed_response_wrapper( + responses.get, + ) diff --git a/src/cloudflare/resources/url_scanner/scans.py b/src/cloudflare/resources/url_scanner/scans.py new file mode 100644 index 00000000000..dc22dbddda8 --- /dev/null +++ b/src/cloudflare/resources/url_scanner/scans.py @@ -0,0 +1,884 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Type, Iterable, cast +from typing_extensions import Literal + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + BinaryAPIResponse, + AsyncBinaryAPIResponse, + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + to_custom_raw_response_wrapper, + async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_raw_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ..._wrappers import ResultWrapper +from ..._base_client import make_request_options +from ...types.url_scanner import scan_list_params, scan_create_params, scan_screenshot_params, scan_bulk_create_params +from ...types.url_scanner.scan_get_response import ScanGetResponse +from ...types.url_scanner.scan_har_response import ScanHARResponse +from ...types.url_scanner.scan_list_response import ScanListResponse +from ...types.url_scanner.scan_create_response import ScanCreateResponse +from ...types.url_scanner.scan_bulk_create_response import ScanBulkCreateResponse + +__all__ = ["ScansResource", "AsyncScansResource"] + + +class ScansResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ScansResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers + """ + return ScansResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ScansResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response + """ + return ScansResourceWithStreamingResponse(self) + + def create( + self, + *, + account_id: str, + url: str, + customagent: str | NotGiven = NOT_GIVEN, + custom_headers: Dict[str, str] | NotGiven = NOT_GIVEN, + referer: str | NotGiven = NOT_GIVEN, + screenshots_resolutions: List[Literal["desktop", "mobile", "tablet"]] | NotGiven = NOT_GIVEN, + visibility: Literal["Public", "Unlisted"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> str: + """Submit a URL to scan. + + Check limits at + https://developers.cloudflare.com/security-center/investigate/scan-limits/. + + Args: + account_id: Account ID. + + custom_headers: Set custom headers. + + screenshots_resolutions: Take multiple screenshots targeting different device types. + + visibility: The option `Public` means it will be included in listings like recent scans and + search results. `Unlisted` means it will not be included in the aforementioned + listings, users will need to have the scan's ID to access it. A a scan will be + automatically marked as unlisted if it fails, if it contains potential PII or + other sensitive material. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + return self._post( + f"/accounts/{account_id}/urlscanner/v2/scan", + body=maybe_transform( + { + "url": url, + "customagent": customagent, + "custom_headers": custom_headers, + "referer": referer, + "screenshots_resolutions": screenshots_resolutions, + "visibility": visibility, + }, + scan_create_params.ScanCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[ScanCreateResponse]._unwrapper, + ), + cast_to=cast(Type[str], ResultWrapper[str]), + ) + + def list( + self, + *, + account_id: str, + q: str | NotGiven = NOT_GIVEN, + size: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ScanListResponse: + """Use a subset of ElasticSearch Query syntax to filter scans. + + Some example + queries:

- 'page.domain:microsoft AND verdicts.malicious:true AND NOT + page.domain:microsoft.com': malicious scans whose hostname starts with + "microsoft".
- 'apikey:me AND date:[2024-01 TO 2024-10]': my scans from 2024 + January to 2024 October.
- 'page.domain:(blogspot OR www.blogspot)': + Searches for scans whose main domain starts with "blogspot" or with + "www.blogspot"
- 'date:>now-7d AND path:okta-sign-in.min.js: scans from the + last 7 days with any request path that ends with "okta-sign-in.min.js"
- + 'page.asn:AS24940 AND hash:xxx': Websites hosted in AS24940 where a resource + with the given hash was downloaded. + + Args: + account_id: Account ID. + + q: Filter scans + + size: Limit the number of objects in the response. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + return self._get( + f"/accounts/{account_id}/urlscanner/v2/search", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "q": q, + "size": size, + }, + scan_list_params.ScanListParams, + ), + ), + cast_to=ScanListResponse, + ) + + def bulk_create( + self, + *, + account_id: str, + body: Iterable[scan_bulk_create_params.Body], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ScanBulkCreateResponse: + """Submit URLs to scan. + + Check limits at + https://developers.cloudflare.com/security-center/investigate/scan-limits/ and + take into account scans submitted in bulk have lower priority and may take + longer to finish. + + Args: + account_id: Account ID. + + body: List of urls to scan (up to a 100). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + return self._post( + f"/accounts/{account_id}/urlscanner/v2/bulk", + body=maybe_transform(body, Iterable[scan_bulk_create_params.Body]), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScanBulkCreateResponse, + ) + + def dom( + self, + scan_id: str, + *, + account_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> str: + """ + Returns a plain text response, with the scan's DOM content as rendered by + Chrome. + + Args: + account_id: Account ID. + + scan_id: Scan UUID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not scan_id: + raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}") + extra_headers = {"Accept": "text/plain", **(extra_headers or {})} + return self._get( + f"/accounts/{account_id}/urlscanner/v2/dom/{scan_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + ) + + def get( + self, + scan_id: str, + *, + account_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ScanGetResponse: + """ + Get URL scan by uuid + + Args: + account_id: Account ID. + + scan_id: Scan UUID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not scan_id: + raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}") + return self._get( + f"/accounts/{account_id}/urlscanner/v2/result/{scan_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScanGetResponse, + ) + + def har( + self, + scan_id: str, + *, + account_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ScanHARResponse: + """Get a URL scan's HAR file. + + See HAR spec at + http://www.softwareishard.com/blog/har-12-spec/. + + Args: + account_id: Account ID. + + scan_id: Scan UUID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not scan_id: + raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}") + return self._get( + f"/accounts/{account_id}/urlscanner/v2/har/{scan_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScanHARResponse, + ) + + def screenshot( + self, + scan_id: str, + *, + account_id: str, + resolution: Literal["desktop", "mobile", "tablet"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BinaryAPIResponse: + """ + Get scan's screenshot by resolution (desktop/mobile/tablet). + + Args: + account_id: Account ID. + + scan_id: Scan UUID. + + resolution: Target device type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not scan_id: + raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}") + extra_headers = {"Accept": "image/png", **(extra_headers or {})} + return self._get( + f"/accounts/{account_id}/urlscanner/v2/screenshots/{scan_id}.png", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"resolution": resolution}, scan_screenshot_params.ScanScreenshotParams), + ), + cast_to=BinaryAPIResponse, + ) + + +class AsyncScansResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncScansResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers + """ + return AsyncScansResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncScansResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response + """ + return AsyncScansResourceWithStreamingResponse(self) + + async def create( + self, + *, + account_id: str, + url: str, + customagent: str | NotGiven = NOT_GIVEN, + custom_headers: Dict[str, str] | NotGiven = NOT_GIVEN, + referer: str | NotGiven = NOT_GIVEN, + screenshots_resolutions: List[Literal["desktop", "mobile", "tablet"]] | NotGiven = NOT_GIVEN, + visibility: Literal["Public", "Unlisted"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> str: + """Submit a URL to scan. + + Check limits at + https://developers.cloudflare.com/security-center/investigate/scan-limits/. + + Args: + account_id: Account ID. + + custom_headers: Set custom headers. + + screenshots_resolutions: Take multiple screenshots targeting different device types. + + visibility: The option `Public` means it will be included in listings like recent scans and + search results. `Unlisted` means it will not be included in the aforementioned + listings, users will need to have the scan's ID to access it. A a scan will be + automatically marked as unlisted if it fails, if it contains potential PII or + other sensitive material. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + return await self._post( + f"/accounts/{account_id}/urlscanner/v2/scan", + body=await async_maybe_transform( + { + "url": url, + "customagent": customagent, + "custom_headers": custom_headers, + "referer": referer, + "screenshots_resolutions": screenshots_resolutions, + "visibility": visibility, + }, + scan_create_params.ScanCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + post_parser=ResultWrapper[ScanCreateResponse]._unwrapper, + ), + cast_to=cast(Type[str], ResultWrapper[str]), + ) + + async def list( + self, + *, + account_id: str, + q: str | NotGiven = NOT_GIVEN, + size: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ScanListResponse: + """Use a subset of ElasticSearch Query syntax to filter scans. + + Some example + queries:

- 'page.domain:microsoft AND verdicts.malicious:true AND NOT + page.domain:microsoft.com': malicious scans whose hostname starts with + "microsoft".
- 'apikey:me AND date:[2024-01 TO 2024-10]': my scans from 2024 + January to 2024 October.
- 'page.domain:(blogspot OR www.blogspot)': + Searches for scans whose main domain starts with "blogspot" or with + "www.blogspot"
- 'date:>now-7d AND path:okta-sign-in.min.js: scans from the + last 7 days with any request path that ends with "okta-sign-in.min.js"
- + 'page.asn:AS24940 AND hash:xxx': Websites hosted in AS24940 where a resource + with the given hash was downloaded. + + Args: + account_id: Account ID. + + q: Filter scans + + size: Limit the number of objects in the response. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + return await self._get( + f"/accounts/{account_id}/urlscanner/v2/search", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "q": q, + "size": size, + }, + scan_list_params.ScanListParams, + ), + ), + cast_to=ScanListResponse, + ) + + async def bulk_create( + self, + *, + account_id: str, + body: Iterable[scan_bulk_create_params.Body], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ScanBulkCreateResponse: + """Submit URLs to scan. + + Check limits at + https://developers.cloudflare.com/security-center/investigate/scan-limits/ and + take into account scans submitted in bulk have lower priority and may take + longer to finish. + + Args: + account_id: Account ID. + + body: List of urls to scan (up to a 100). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + return await self._post( + f"/accounts/{account_id}/urlscanner/v2/bulk", + body=await async_maybe_transform(body, Iterable[scan_bulk_create_params.Body]), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScanBulkCreateResponse, + ) + + async def dom( + self, + scan_id: str, + *, + account_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> str: + """ + Returns a plain text response, with the scan's DOM content as rendered by + Chrome. + + Args: + account_id: Account ID. + + scan_id: Scan UUID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not scan_id: + raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}") + extra_headers = {"Accept": "text/plain", **(extra_headers or {})} + return await self._get( + f"/accounts/{account_id}/urlscanner/v2/dom/{scan_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=str, + ) + + async def get( + self, + scan_id: str, + *, + account_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ScanGetResponse: + """ + Get URL scan by uuid + + Args: + account_id: Account ID. + + scan_id: Scan UUID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not scan_id: + raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}") + return await self._get( + f"/accounts/{account_id}/urlscanner/v2/result/{scan_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScanGetResponse, + ) + + async def har( + self, + scan_id: str, + *, + account_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ScanHARResponse: + """Get a URL scan's HAR file. + + See HAR spec at + http://www.softwareishard.com/blog/har-12-spec/. + + Args: + account_id: Account ID. + + scan_id: Scan UUID. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not scan_id: + raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}") + return await self._get( + f"/accounts/{account_id}/urlscanner/v2/har/{scan_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScanHARResponse, + ) + + async def screenshot( + self, + scan_id: str, + *, + account_id: str, + resolution: Literal["desktop", "mobile", "tablet"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncBinaryAPIResponse: + """ + Get scan's screenshot by resolution (desktop/mobile/tablet). + + Args: + account_id: Account ID. + + scan_id: Scan UUID. + + resolution: Target device type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not account_id: + raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") + if not scan_id: + raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}") + extra_headers = {"Accept": "image/png", **(extra_headers or {})} + return await self._get( + f"/accounts/{account_id}/urlscanner/v2/screenshots/{scan_id}.png", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"resolution": resolution}, scan_screenshot_params.ScanScreenshotParams + ), + ), + cast_to=AsyncBinaryAPIResponse, + ) + + +class ScansResourceWithRawResponse: + def __init__(self, scans: ScansResource) -> None: + self._scans = scans + + self.create = to_raw_response_wrapper( + scans.create, + ) + self.list = to_raw_response_wrapper( + scans.list, + ) + self.bulk_create = to_raw_response_wrapper( + scans.bulk_create, + ) + self.dom = to_raw_response_wrapper( + scans.dom, + ) + self.get = to_raw_response_wrapper( + scans.get, + ) + self.har = to_raw_response_wrapper( + scans.har, + ) + self.screenshot = to_custom_raw_response_wrapper( + scans.screenshot, + BinaryAPIResponse, + ) + + +class AsyncScansResourceWithRawResponse: + def __init__(self, scans: AsyncScansResource) -> None: + self._scans = scans + + self.create = async_to_raw_response_wrapper( + scans.create, + ) + self.list = async_to_raw_response_wrapper( + scans.list, + ) + self.bulk_create = async_to_raw_response_wrapper( + scans.bulk_create, + ) + self.dom = async_to_raw_response_wrapper( + scans.dom, + ) + self.get = async_to_raw_response_wrapper( + scans.get, + ) + self.har = async_to_raw_response_wrapper( + scans.har, + ) + self.screenshot = async_to_custom_raw_response_wrapper( + scans.screenshot, + AsyncBinaryAPIResponse, + ) + + +class ScansResourceWithStreamingResponse: + def __init__(self, scans: ScansResource) -> None: + self._scans = scans + + self.create = to_streamed_response_wrapper( + scans.create, + ) + self.list = to_streamed_response_wrapper( + scans.list, + ) + self.bulk_create = to_streamed_response_wrapper( + scans.bulk_create, + ) + self.dom = to_streamed_response_wrapper( + scans.dom, + ) + self.get = to_streamed_response_wrapper( + scans.get, + ) + self.har = to_streamed_response_wrapper( + scans.har, + ) + self.screenshot = to_custom_streamed_response_wrapper( + scans.screenshot, + StreamedBinaryAPIResponse, + ) + + +class AsyncScansResourceWithStreamingResponse: + def __init__(self, scans: AsyncScansResource) -> None: + self._scans = scans + + self.create = async_to_streamed_response_wrapper( + scans.create, + ) + self.list = async_to_streamed_response_wrapper( + scans.list, + ) + self.bulk_create = async_to_streamed_response_wrapper( + scans.bulk_create, + ) + self.dom = async_to_streamed_response_wrapper( + scans.dom, + ) + self.get = async_to_streamed_response_wrapper( + scans.get, + ) + self.har = async_to_streamed_response_wrapper( + scans.har, + ) + self.screenshot = async_to_custom_streamed_response_wrapper( + scans.screenshot, + AsyncStreamedBinaryAPIResponse, + ) diff --git a/src/cloudflare/resources/url_scanner/url_scanner.py b/src/cloudflare/resources/url_scanner/url_scanner.py new file mode 100644 index 00000000000..599de25507c --- /dev/null +++ b/src/cloudflare/resources/url_scanner/url_scanner.py @@ -0,0 +1,134 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .scans import ( + ScansResource, + AsyncScansResource, + ScansResourceWithRawResponse, + AsyncScansResourceWithRawResponse, + ScansResourceWithStreamingResponse, + AsyncScansResourceWithStreamingResponse, +) +from ..._compat import cached_property +from .responses import ( + ResponsesResource, + AsyncResponsesResource, + ResponsesResourceWithRawResponse, + AsyncResponsesResourceWithRawResponse, + ResponsesResourceWithStreamingResponse, + AsyncResponsesResourceWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["URLScannerResource", "AsyncURLScannerResource"] + + +class URLScannerResource(SyncAPIResource): + @cached_property + def responses(self) -> ResponsesResource: + return ResponsesResource(self._client) + + @cached_property + def scans(self) -> ScansResource: + return ScansResource(self._client) + + @cached_property + def with_raw_response(self) -> URLScannerResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers + """ + return URLScannerResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> URLScannerResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response + """ + return URLScannerResourceWithStreamingResponse(self) + + +class AsyncURLScannerResource(AsyncAPIResource): + @cached_property + def responses(self) -> AsyncResponsesResource: + return AsyncResponsesResource(self._client) + + @cached_property + def scans(self) -> AsyncScansResource: + return AsyncScansResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncURLScannerResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return the + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers + """ + return AsyncURLScannerResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncURLScannerResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response + """ + return AsyncURLScannerResourceWithStreamingResponse(self) + + +class URLScannerResourceWithRawResponse: + def __init__(self, url_scanner: URLScannerResource) -> None: + self._url_scanner = url_scanner + + @cached_property + def responses(self) -> ResponsesResourceWithRawResponse: + return ResponsesResourceWithRawResponse(self._url_scanner.responses) + + @cached_property + def scans(self) -> ScansResourceWithRawResponse: + return ScansResourceWithRawResponse(self._url_scanner.scans) + + +class AsyncURLScannerResourceWithRawResponse: + def __init__(self, url_scanner: AsyncURLScannerResource) -> None: + self._url_scanner = url_scanner + + @cached_property + def responses(self) -> AsyncResponsesResourceWithRawResponse: + return AsyncResponsesResourceWithRawResponse(self._url_scanner.responses) + + @cached_property + def scans(self) -> AsyncScansResourceWithRawResponse: + return AsyncScansResourceWithRawResponse(self._url_scanner.scans) + + +class URLScannerResourceWithStreamingResponse: + def __init__(self, url_scanner: URLScannerResource) -> None: + self._url_scanner = url_scanner + + @cached_property + def responses(self) -> ResponsesResourceWithStreamingResponse: + return ResponsesResourceWithStreamingResponse(self._url_scanner.responses) + + @cached_property + def scans(self) -> ScansResourceWithStreamingResponse: + return ScansResourceWithStreamingResponse(self._url_scanner.scans) + + +class AsyncURLScannerResourceWithStreamingResponse: + def __init__(self, url_scanner: AsyncURLScannerResource) -> None: + self._url_scanner = url_scanner + + @cached_property + def responses(self) -> AsyncResponsesResourceWithStreamingResponse: + return AsyncResponsesResourceWithStreamingResponse(self._url_scanner.responses) + + @cached_property + def scans(self) -> AsyncScansResourceWithStreamingResponse: + return AsyncScansResourceWithStreamingResponse(self._url_scanner.scans) diff --git a/src/cloudflare/types/url_scanner/__init__.py b/src/cloudflare/types/url_scanner/__init__.py index f8ee8b14b1c..988295382a6 100644 --- a/src/cloudflare/types/url_scanner/__init__.py +++ b/src/cloudflare/types/url_scanner/__init__.py @@ -1,3 +1,15 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations + +from .scan_list_params import ScanListParams as ScanListParams +from .scan_dom_response import ScanDOMResponse as ScanDOMResponse +from .scan_get_response import ScanGetResponse as ScanGetResponse +from .scan_har_response import ScanHARResponse as ScanHARResponse +from .scan_create_params import ScanCreateParams as ScanCreateParams +from .scan_list_response import ScanListResponse as ScanListResponse +from .scan_create_response import ScanCreateResponse as ScanCreateResponse +from .response_get_response import ResponseGetResponse as ResponseGetResponse +from .scan_screenshot_params import ScanScreenshotParams as ScanScreenshotParams +from .scan_bulk_create_params import ScanBulkCreateParams as ScanBulkCreateParams +from .scan_bulk_create_response import ScanBulkCreateResponse as ScanBulkCreateResponse diff --git a/src/cloudflare/types/url_scanner/response_get_response.py b/src/cloudflare/types/url_scanner/response_get_response.py new file mode 100644 index 00000000000..c2215a79037 --- /dev/null +++ b/src/cloudflare/types/url_scanner/response_get_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["ResponseGetResponse"] + +ResponseGetResponse: TypeAlias = str diff --git a/src/cloudflare/types/url_scanner/scan_bulk_create_params.py b/src/cloudflare/types/url_scanner/scan_bulk_create_params.py new file mode 100644 index 00000000000..6509c75550e --- /dev/null +++ b/src/cloudflare/types/url_scanner/scan_bulk_create_params.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Iterable +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ScanBulkCreateParams", "Body"] + + +class ScanBulkCreateParams(TypedDict, total=False): + account_id: Required[str] + """Account ID.""" + + body: Required[Iterable[Body]] + """List of urls to scan (up to a 100).""" + + +class Body(TypedDict, total=False): + url: Required[str] + + customagent: str + + custom_headers: Annotated[Dict[str, str], PropertyInfo(alias="customHeaders")] + """Set custom headers.""" + + referer: str + + screenshots_resolutions: Annotated[ + List[Literal["desktop", "mobile", "tablet"]], PropertyInfo(alias="screenshotsResolutions") + ] + """Take multiple screenshots targeting different device types.""" + + visibility: Literal["Public", "Unlisted"] + """ + The option `Public` means it will be included in listings like recent scans and + search results. `Unlisted` means it will not be included in the aforementioned + listings, users will need to have the scan's ID to access it. A a scan will be + automatically marked as unlisted if it fails, if it contains potential PII or + other sensitive material. + """ diff --git a/src/cloudflare/types/url_scanner/scan_bulk_create_response.py b/src/cloudflare/types/url_scanner/scan_bulk_create_response.py new file mode 100644 index 00000000000..6a554a9878b --- /dev/null +++ b/src/cloudflare/types/url_scanner/scan_bulk_create_response.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import TypeAlias + +from ..._models import BaseModel + +__all__ = ["ScanBulkCreateResponse", "ScanBulkCreateResponseItem", "ScanBulkCreateResponseItemOptions"] + + +class ScanBulkCreateResponseItemOptions(BaseModel): + useragent: Optional[str] = None + + +class ScanBulkCreateResponseItem(BaseModel): + api: str + """URL to api report.""" + + result: str + """URL to report.""" + + url: str + """Submitted URL""" + + uuid: str + """Scan ID.""" + + visibility: str + """Submitted visibility status.""" + + options: Optional[ScanBulkCreateResponseItemOptions] = None + + +ScanBulkCreateResponse: TypeAlias = List[ScanBulkCreateResponseItem] diff --git a/src/cloudflare/types/url_scanner/scan_create_params.py b/src/cloudflare/types/url_scanner/scan_create_params.py new file mode 100644 index 00000000000..782da4a9c85 --- /dev/null +++ b/src/cloudflare/types/url_scanner/scan_create_params.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ScanCreateParams"] + + +class ScanCreateParams(TypedDict, total=False): + account_id: Required[str] + """Account ID.""" + + url: Required[str] + + customagent: str + + custom_headers: Annotated[Dict[str, str], PropertyInfo(alias="customHeaders")] + """Set custom headers.""" + + referer: str + + screenshots_resolutions: Annotated[ + List[Literal["desktop", "mobile", "tablet"]], PropertyInfo(alias="screenshotsResolutions") + ] + """Take multiple screenshots targeting different device types.""" + + visibility: Literal["Public", "Unlisted"] + """ + The option `Public` means it will be included in listings like recent scans and + search results. `Unlisted` means it will not be included in the aforementioned + listings, users will need to have the scan's ID to access it. A a scan will be + automatically marked as unlisted if it fails, if it contains potential PII or + other sensitive material. + """ diff --git a/src/cloudflare/types/url_scanner/scan_create_response.py b/src/cloudflare/types/url_scanner/scan_create_response.py new file mode 100644 index 00000000000..4534ad1039a --- /dev/null +++ b/src/cloudflare/types/url_scanner/scan_create_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["ScanCreateResponse"] + +ScanCreateResponse: TypeAlias = str diff --git a/src/cloudflare/types/url_scanner/scan_dom_response.py b/src/cloudflare/types/url_scanner/scan_dom_response.py new file mode 100644 index 00000000000..3d53cb81dbf --- /dev/null +++ b/src/cloudflare/types/url_scanner/scan_dom_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["ScanDOMResponse"] + +ScanDOMResponse: TypeAlias = str diff --git a/src/cloudflare/types/url_scanner/scan_get_response.py b/src/cloudflare/types/url_scanner/scan_get_response.py new file mode 100644 index 00000000000..8d1ef4a39e0 --- /dev/null +++ b/src/cloudflare/types/url_scanner/scan_get_response.py @@ -0,0 +1,859 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = [ + "ScanGetResponse", + "Data", + "DataConsole", + "DataConsoleMessage", + "DataCookie", + "DataGlobal", + "DataLink", + "DataPerformance", + "DataRequest", + "DataRequestRequest", + "DataRequestRequestInitiator", + "DataRequestRequestRequest", + "DataRequestRequestRedirectResponse", + "DataRequestRequestRedirectResponseSecurityHeader", + "DataRequestResponse", + "DataRequestResponseASN", + "DataRequestResponseGeoip", + "DataRequestResponseResponse", + "DataRequestResponseResponseSecurityDetails", + "DataRequestResponseResponseSecurityHeader", + "Lists", + "ListsCertificate", + "Meta", + "MetaProcessors", + "MetaProcessorsASN", + "MetaProcessorsASNData", + "MetaProcessorsDNS", + "MetaProcessorsDNSData", + "MetaProcessorsDomainCategories", + "MetaProcessorsDomainCategoriesData", + "MetaProcessorsGeoip", + "MetaProcessorsGeoipData", + "MetaProcessorsGeoipDataGeoip", + "MetaProcessorsPhishing", + "MetaProcessorsRadarRank", + "MetaProcessorsRadarRankData", + "MetaProcessorsWappa", + "MetaProcessorsWappaData", + "MetaProcessorsWappaDataCategory", + "MetaProcessorsWappaDataConfidence", + "MetaProcessorsURLCategories", + "MetaProcessorsURLCategoriesData", + "MetaProcessorsURLCategoriesDataContent", + "MetaProcessorsURLCategoriesDataInherited", + "MetaProcessorsURLCategoriesDataInheritedContent", + "MetaProcessorsURLCategoriesDataInheritedRisk", + "MetaProcessorsURLCategoriesDataRisk", + "Page", + "PageScreenshot", + "Scanner", + "Stats", + "StatsDomainStat", + "StatsIPStat", + "StatsIPStatASN", + "StatsIPStatGeoip", + "StatsProtocolStat", + "StatsResourceStat", + "StatsServerStat", + "StatsTLSStat", + "StatsTLSStatProtocols", + "Task", + "TaskOptions", + "Verdicts", + "VerdictsOverall", +] + + +class DataConsoleMessage(BaseModel): + level: str + + source: str + + text: str + + url: str + + +class DataConsole(BaseModel): + message: DataConsoleMessage + + +class DataCookie(BaseModel): + domain: str + + expires: float + + http_only: bool = FieldInfo(alias="httpOnly") + + name: str + + path: str + + priority: str + + same_party: bool = FieldInfo(alias="sameParty") + + secure: bool + + session: bool + + size: float + + source_port: float = FieldInfo(alias="sourcePort") + + source_scheme: str = FieldInfo(alias="sourceScheme") + + value: str + + +class DataGlobal(BaseModel): + prop: str + + type: str + + +class DataLink(BaseModel): + href: str + + text: str + + +class DataPerformance(BaseModel): + duration: float + + entry_type: str = FieldInfo(alias="entryType") + + name: str + + start_time: float = FieldInfo(alias="startTime") + + +class DataRequestRequestInitiator(BaseModel): + host: str + + type: str + + url: str + + +class DataRequestRequestRequest(BaseModel): + initial_priority: str = FieldInfo(alias="initialPriority") + + is_same_site: bool = FieldInfo(alias="isSameSite") + + method: str + + mixed_content_type: str = FieldInfo(alias="mixedContentType") + + referrer_policy: str = FieldInfo(alias="referrerPolicy") + + url: str + + headers: Optional[object] = None + + +class DataRequestRequestRedirectResponseSecurityHeader(BaseModel): + name: str + + value: str + + +class DataRequestRequestRedirectResponse(BaseModel): + charset: str + + mime_type: str = FieldInfo(alias="mimeType") + + protocol: str + + remote_ip_address: str = FieldInfo(alias="remoteIPAddress") + + remote_port: float = FieldInfo(alias="remotePort") + + security_headers: List[DataRequestRequestRedirectResponseSecurityHeader] = FieldInfo(alias="securityHeaders") + + security_state: str = FieldInfo(alias="securityState") + + status: float + + status_text: str = FieldInfo(alias="statusText") + + url: str + + headers: Optional[object] = None + + +class DataRequestRequest(BaseModel): + document_url: str = FieldInfo(alias="documentURL") + + has_user_gesture: bool = FieldInfo(alias="hasUserGesture") + + initiator: DataRequestRequestInitiator + + redirect_has_extra_info: bool = FieldInfo(alias="redirectHasExtraInfo") + + request: DataRequestRequestRequest + + request_id: str = FieldInfo(alias="requestId") + + type: str + + wall_time: float = FieldInfo(alias="wallTime") + + frame_id: Optional[str] = FieldInfo(alias="frameId", default=None) + + loader_id: Optional[str] = FieldInfo(alias="loaderId", default=None) + + primary_request: Optional[bool] = FieldInfo(alias="primaryRequest", default=None) + + redirect_response: Optional[DataRequestRequestRedirectResponse] = FieldInfo(alias="redirectResponse", default=None) + + +class DataRequestResponseASN(BaseModel): + asn: str + + country: str + + description: str + + ip: str + + name: str + + org: str + + +class DataRequestResponseGeoip(BaseModel): + city: str + + country: str + + country_name: str + + geoname_id: str = FieldInfo(alias="geonameId") + + ll: List[object] + + region: str + + +class DataRequestResponseResponseSecurityDetails(BaseModel): + certificate_id: float = FieldInfo(alias="certificateId") + + certificate_transparency_compliance: str = FieldInfo(alias="certificateTransparencyCompliance") + + cipher: str + + encrypted_client_hello: bool = FieldInfo(alias="encryptedClientHello") + + issuer: str + + key_exchange: str = FieldInfo(alias="keyExchange") + + key_exchange_group: str = FieldInfo(alias="keyExchangeGroup") + + protocol: str + + san_list: List[str] = FieldInfo(alias="sanList") + + server_signature_algorithm: float = FieldInfo(alias="serverSignatureAlgorithm") + + subject_name: str = FieldInfo(alias="subjectName") + + valid_from: float = FieldInfo(alias="validFrom") + + valid_to: float = FieldInfo(alias="validTo") + + +class DataRequestResponseResponseSecurityHeader(BaseModel): + name: str + + value: str + + +class DataRequestResponseResponse(BaseModel): + charset: str + + mime_type: str = FieldInfo(alias="mimeType") + + protocol: str + + remote_ip_address: str = FieldInfo(alias="remoteIPAddress") + + remote_port: float = FieldInfo(alias="remotePort") + + security_details: DataRequestResponseResponseSecurityDetails = FieldInfo(alias="securityDetails") + + security_headers: List[DataRequestResponseResponseSecurityHeader] = FieldInfo(alias="securityHeaders") + + security_state: str = FieldInfo(alias="securityState") + + status: float + + status_text: str = FieldInfo(alias="statusText") + + url: str + + headers: Optional[object] = None + + +class DataRequestResponse(BaseModel): + asn: DataRequestResponseASN + + data_length: float = FieldInfo(alias="dataLength") + + encoded_data_length: float = FieldInfo(alias="encodedDataLength") + + geoip: DataRequestResponseGeoip + + has_extra_info: bool = FieldInfo(alias="hasExtraInfo") + + request_id: str = FieldInfo(alias="requestId") + + response: DataRequestResponseResponse + + size: float + + type: str + + content_available: Optional[bool] = FieldInfo(alias="contentAvailable", default=None) + + hash: Optional[str] = None + + +class DataRequest(BaseModel): + request: DataRequestRequest + + response: DataRequestResponse + + requests: Optional[List[DataRequestRequest]] = None + + +class Data(BaseModel): + console: List[DataConsole] + + cookies: List[DataCookie] + + globals: List[DataGlobal] + + links: List[DataLink] + + performance: List[DataPerformance] + + requests: List[DataRequest] + + +class ListsCertificate(BaseModel): + issuer: str + + subject_name: str = FieldInfo(alias="subjectName") + + valid_from: float = FieldInfo(alias="validFrom") + + valid_to: float = FieldInfo(alias="validTo") + + +class Lists(BaseModel): + asns: List[str] + + certificates: List[ListsCertificate] + + continents: List[str] + + countries: List[str] + + domains: List[str] + + hashes: List[str] + + ips: List[str] + + link_domains: List[str] = FieldInfo(alias="linkDomains") + + servers: List[str] + + urls: List[str] + + +class MetaProcessorsASNData(BaseModel): + asn: str + + country: str + + description: str + + ip: str + + name: str + + +class MetaProcessorsASN(BaseModel): + data: List[MetaProcessorsASNData] + + +class MetaProcessorsDNSData(BaseModel): + address: str + + dnssec_valid: bool + + name: str + + type: str + + +class MetaProcessorsDNS(BaseModel): + data: List[MetaProcessorsDNSData] + + +class MetaProcessorsDomainCategoriesData(BaseModel): + inherited: object + + is_primary: bool = FieldInfo(alias="isPrimary") + + name: str + + +class MetaProcessorsDomainCategories(BaseModel): + data: List[MetaProcessorsDomainCategoriesData] + + +class MetaProcessorsGeoipDataGeoip(BaseModel): + city: str + + country: str + + country_name: str + + ll: List[float] + + region: str + + +class MetaProcessorsGeoipData(BaseModel): + geoip: MetaProcessorsGeoipDataGeoip + + ip: str + + +class MetaProcessorsGeoip(BaseModel): + data: List[MetaProcessorsGeoipData] + + +class MetaProcessorsPhishing(BaseModel): + data: List[str] + + +class MetaProcessorsRadarRankData(BaseModel): + bucket: str + + hostname: str + + rank: Optional[float] = None + + +class MetaProcessorsRadarRank(BaseModel): + data: List[MetaProcessorsRadarRankData] + + +class MetaProcessorsWappaDataCategory(BaseModel): + name: str + + priority: float + + +class MetaProcessorsWappaDataConfidence(BaseModel): + confidence: float + + name: str + + pattern: str + + pattern_type: str = FieldInfo(alias="patternType") + + +class MetaProcessorsWappaData(BaseModel): + app: str + + categories: List[MetaProcessorsWappaDataCategory] + + confidence: List[MetaProcessorsWappaDataConfidence] + + confidence_total: float = FieldInfo(alias="confidenceTotal") + + icon: str + + website: str + + +class MetaProcessorsWappa(BaseModel): + data: List[MetaProcessorsWappaData] + + +class MetaProcessorsURLCategoriesDataContent(BaseModel): + id: float + + name: str + + super_category_id: float + + +class MetaProcessorsURLCategoriesDataInheritedContent(BaseModel): + id: float + + name: str + + super_category_id: float + + +class MetaProcessorsURLCategoriesDataInheritedRisk(BaseModel): + id: float + + name: str + + super_category_id: float + + +class MetaProcessorsURLCategoriesDataInherited(BaseModel): + content: List[MetaProcessorsURLCategoriesDataInheritedContent] + + from_: str = FieldInfo(alias="from") + + risks: List[MetaProcessorsURLCategoriesDataInheritedRisk] + + +class MetaProcessorsURLCategoriesDataRisk(BaseModel): + id: float + + name: str + + super_category_id: float + + +class MetaProcessorsURLCategoriesData(BaseModel): + content: List[MetaProcessorsURLCategoriesDataContent] + + inherited: MetaProcessorsURLCategoriesDataInherited + + name: str + + risks: List[MetaProcessorsURLCategoriesDataRisk] + + +class MetaProcessorsURLCategories(BaseModel): + data: List[MetaProcessorsURLCategoriesData] + + +class MetaProcessors(BaseModel): + asn: MetaProcessorsASN + + dns: MetaProcessorsDNS + + domain_categories: MetaProcessorsDomainCategories = FieldInfo(alias="domainCategories") + + geoip: MetaProcessorsGeoip + + phishing: MetaProcessorsPhishing + + radar_rank: MetaProcessorsRadarRank = FieldInfo(alias="radarRank") + + wappa: MetaProcessorsWappa + + url_categories: Optional[MetaProcessorsURLCategories] = FieldInfo(alias="urlCategories", default=None) + + +class Meta(BaseModel): + processors: MetaProcessors + + +class PageScreenshot(BaseModel): + dhash: str + + mm3_hash: float = FieldInfo(alias="mm3Hash") + + name: str + + phash: str + + +class Page(BaseModel): + apex_domain: str = FieldInfo(alias="apexDomain") + + asn: str + + asnname: str + + city: str + + country: str + + domain: str + + ip: str + + mime_type: str = FieldInfo(alias="mimeType") + + server: str + + status: str + + title: str + + tls_age_days: float = FieldInfo(alias="tlsAgeDays") + + tls_issuer: str = FieldInfo(alias="tlsIssuer") + + tls_valid_days: float = FieldInfo(alias="tlsValidDays") + + tls_valid_from: str = FieldInfo(alias="tlsValidFrom") + + url: str + + screenshot: Optional[PageScreenshot] = None + + +class Scanner(BaseModel): + colo: str + + country: str + + +class StatsDomainStat(BaseModel): + count: float + + countries: List[str] + + domain: str + + encoded_size: float = FieldInfo(alias="encodedSize") + + index: float + + initiators: List[str] + + ips: List[str] + + redirects: float + + size: float + + +class StatsIPStatASN(BaseModel): + asn: str + + country: str + + description: str + + ip: str + + name: str + + org: str + + +class StatsIPStatGeoip(BaseModel): + city: str + + country: str + + country_name: str + + ll: List[float] + + region: str + + +class StatsIPStat(BaseModel): + asn: StatsIPStatASN + + countries: List[str] + + domains: List[str] + + encoded_size: float = FieldInfo(alias="encodedSize") + + geoip: StatsIPStatGeoip + + index: float + + ip: str + + ipv6: bool + + redirects: float + + requests: float + + size: float + + count: Optional[float] = None + + +class StatsProtocolStat(BaseModel): + count: float + + countries: List[str] + + encoded_size: float = FieldInfo(alias="encodedSize") + + ips: List[str] + + protocol: str + + size: float + + +class StatsResourceStat(BaseModel): + compression: float + + count: float + + countries: List[str] + + encoded_size: float = FieldInfo(alias="encodedSize") + + ips: List[str] + + percentage: float + + size: float + + type: str + + +class StatsServerStat(BaseModel): + count: float + + countries: List[str] + + encoded_size: float = FieldInfo(alias="encodedSize") + + ips: List[str] + + server: str + + size: float + + +class StatsTLSStatProtocols(BaseModel): + tls_1_3_aes_128_gcm: float = FieldInfo(alias="TLS 1.3 / AES_128_GCM") + + +class StatsTLSStat(BaseModel): + count: float + + countries: List[str] + + encoded_size: float = FieldInfo(alias="encodedSize") + + ips: List[str] + + protocols: StatsTLSStatProtocols + + security_state: str = FieldInfo(alias="securityState") + + size: float + + +class Stats(BaseModel): + domain_stats: List[StatsDomainStat] = FieldInfo(alias="domainStats") + + ip_stats: List[StatsIPStat] = FieldInfo(alias="ipStats") + + i_pv6_percentage: float = FieldInfo(alias="IPv6Percentage") + + malicious: float + + protocol_stats: List[StatsProtocolStat] = FieldInfo(alias="protocolStats") + + resource_stats: List[StatsResourceStat] = FieldInfo(alias="resourceStats") + + secure_percentage: float = FieldInfo(alias="securePercentage") + + secure_requests: float = FieldInfo(alias="secureRequests") + + server_stats: List[StatsServerStat] = FieldInfo(alias="serverStats") + + tls_stats: List[StatsTLSStat] = FieldInfo(alias="tlsStats") + + total_links: float = FieldInfo(alias="totalLinks") + + uniq_asns: float = FieldInfo(alias="uniqASNs") + + uniq_countries: float = FieldInfo(alias="uniqCountries") + + +class TaskOptions(BaseModel): + custom_headers: Optional[object] = FieldInfo(alias="customHeaders", default=None) + """Custom headers set.""" + + screenshots_resolutions: Optional[List[str]] = FieldInfo(alias="screenshotsResolutions", default=None) + + +class Task(BaseModel): + apex_domain: str = FieldInfo(alias="apexDomain") + + domain: str + + dom_url: str = FieldInfo(alias="domURL") + + method: str + + options: TaskOptions + + report_url: str = FieldInfo(alias="reportURL") + + screenshot_url: str = FieldInfo(alias="screenshotURL") + + source: str + + success: bool + + time: str + + url: str + + uuid: str + + visibility: str + + +class VerdictsOverall(BaseModel): + categories: List[str] + + has_verdicts: bool = FieldInfo(alias="hasVerdicts") + + malicious: bool + + tags: List[str] + + +class Verdicts(BaseModel): + overall: VerdictsOverall + + +class ScanGetResponse(BaseModel): + data: Data + + lists: Lists + + meta: Meta + + page: Page + + scanner: Scanner + + stats: Stats + + task: Task + + verdicts: Verdicts diff --git a/src/cloudflare/types/url_scanner/scan_har_response.py b/src/cloudflare/types/url_scanner/scan_har_response.py new file mode 100644 index 00000000000..2a4f344de77 --- /dev/null +++ b/src/cloudflare/types/url_scanner/scan_har_response.py @@ -0,0 +1,143 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = [ + "ScanHARResponse", + "Log", + "LogCreator", + "LogEntry", + "LogEntryRequest", + "LogEntryRequestHeader", + "LogEntryResponse", + "LogEntryResponseContent", + "LogEntryResponseHeader", + "LogPage", + "LogPagePageTimings", +] + + +class LogCreator(BaseModel): + comment: str + + name: str + + version: str + + +class LogEntryRequestHeader(BaseModel): + name: str + + value: str + + +class LogEntryRequest(BaseModel): + body_size: float = FieldInfo(alias="bodySize") + + headers: List[LogEntryRequestHeader] + + headers_size: float = FieldInfo(alias="headersSize") + + http_version: str = FieldInfo(alias="httpVersion") + + method: str + + url: str + + +class LogEntryResponseContent(BaseModel): + mime_type: str = FieldInfo(alias="mimeType") + + size: float + + compression: Optional[int] = None + + +class LogEntryResponseHeader(BaseModel): + name: str + + value: str + + +class LogEntryResponse(BaseModel): + transfer_size: float = FieldInfo(alias="_transferSize") + + body_size: float = FieldInfo(alias="bodySize") + + content: LogEntryResponseContent + + headers: List[LogEntryResponseHeader] + + headers_size: float = FieldInfo(alias="headersSize") + + http_version: str = FieldInfo(alias="httpVersion") + + redirect_url: str = FieldInfo(alias="redirectURL") + + status: float + + status_text: str = FieldInfo(alias="statusText") + + +class LogEntry(BaseModel): + initial_priority: str = FieldInfo(alias="_initialPriority") + + initiator_type: str = FieldInfo(alias="_initiator_type") + + priority: str = FieldInfo(alias="_priority") + + request_id: str = FieldInfo(alias="_requestId") + + request_time: float = FieldInfo(alias="_requestTime") + + resource_type: str = FieldInfo(alias="_resourceType") + + cache: object + + connection: str + + pageref: str + + request: LogEntryRequest + + response: LogEntryResponse + + server_ip_address: str = FieldInfo(alias="serverIPAddress") + + started_date_time: str = FieldInfo(alias="startedDateTime") + + time: float + + +class LogPagePageTimings(BaseModel): + on_content_load: float = FieldInfo(alias="onContentLoad") + + on_load: float = FieldInfo(alias="onLoad") + + +class LogPage(BaseModel): + id: str + + page_timings: LogPagePageTimings = FieldInfo(alias="pageTimings") + + started_date_time: str = FieldInfo(alias="startedDateTime") + + title: str + + +class Log(BaseModel): + creator: LogCreator + + entries: List[LogEntry] + + pages: List[LogPage] + + version: str + + +class ScanHARResponse(BaseModel): + log: Log diff --git a/src/cloudflare/types/url_scanner/scan_list_params.py b/src/cloudflare/types/url_scanner/scan_list_params.py new file mode 100644 index 00000000000..97421b92ccc --- /dev/null +++ b/src/cloudflare/types/url_scanner/scan_list_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ScanListParams"] + + +class ScanListParams(TypedDict, total=False): + account_id: Required[str] + """Account ID.""" + + q: str + """Filter scans""" + + size: int + """Limit the number of objects in the response.""" diff --git a/src/cloudflare/types/url_scanner/scan_list_response.py b/src/cloudflare/types/url_scanner/scan_list_response.py new file mode 100644 index 00000000000..7cb1be617a4 --- /dev/null +++ b/src/cloudflare/types/url_scanner/scan_list_response.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["ScanListResponse", "Result", "ResultPage", "ResultStats", "ResultTask", "ResultVerdicts"] + + +class ResultPage(BaseModel): + asn: str + + country: str + + ip: str + + url: str + + +class ResultStats(BaseModel): + data_length: float = FieldInfo(alias="dataLength") + + requests: float + + uniq_countries: float = FieldInfo(alias="uniqCountries") + + uniq_ips: float = FieldInfo(alias="uniqIPs") + + +class ResultTask(BaseModel): + time: str + + url: str + + uuid: str + + visibility: str + + +class ResultVerdicts(BaseModel): + malicious: bool + + +class Result(BaseModel): + api_id: str = FieldInfo(alias="_id") + + page: ResultPage + + result: str + + stats: ResultStats + + task: ResultTask + + verdicts: ResultVerdicts + + +class ScanListResponse(BaseModel): + results: List[Result] diff --git a/src/cloudflare/types/url_scanner/scan_screenshot_params.py b/src/cloudflare/types/url_scanner/scan_screenshot_params.py new file mode 100644 index 00000000000..263a003363f --- /dev/null +++ b/src/cloudflare/types/url_scanner/scan_screenshot_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ScanScreenshotParams"] + + +class ScanScreenshotParams(TypedDict, total=False): + account_id: Required[str] + """Account ID.""" + + resolution: Literal["desktop", "mobile", "tablet"] + """Target device type.""" diff --git a/tests/api_resources/url_scanner/__init__.py b/tests/api_resources/url_scanner/__init__.py new file mode 100644 index 00000000000..fd8019a9a1a --- /dev/null +++ b/tests/api_resources/url_scanner/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/url_scanner/test_responses.py b/tests/api_resources/url_scanner/test_responses.py new file mode 100644 index 00000000000..1690ba966df --- /dev/null +++ b/tests/api_resources/url_scanner/test_responses.py @@ -0,0 +1,117 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from cloudflare import Cloudflare, AsyncCloudflare +from tests.utils import assert_matches_type + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestResponses: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_get(self, client: Cloudflare) -> None: + response = client.url_scanner.responses.get( + response_id="response_id", + account_id="account_id", + ) + assert_matches_type(str, response, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Cloudflare) -> None: + http_response = client.url_scanner.responses.with_raw_response.get( + response_id="response_id", + account_id="account_id", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(str, response, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Cloudflare) -> None: + with client.url_scanner.responses.with_streaming_response.get( + response_id="response_id", + account_id="account_id", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = http_response.parse() + assert_matches_type(str, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + def test_path_params_get(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.url_scanner.responses.with_raw_response.get( + response_id="response_id", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + client.url_scanner.responses.with_raw_response.get( + response_id="", + account_id="account_id", + ) + + +class TestAsyncResponses: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_get(self, async_client: AsyncCloudflare) -> None: + response = await async_client.url_scanner.responses.get( + response_id="response_id", + account_id="account_id", + ) + assert_matches_type(str, response, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None: + http_response = await async_client.url_scanner.responses.with_raw_response.get( + response_id="response_id", + account_id="account_id", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = await http_response.parse() + assert_matches_type(str, response, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None: + async with async_client.url_scanner.responses.with_streaming_response.get( + response_id="response_id", + account_id="account_id", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = await http_response.parse() + assert_matches_type(str, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + async def test_path_params_get(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.url_scanner.responses.with_raw_response.get( + response_id="response_id", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + await async_client.url_scanner.responses.with_raw_response.get( + response_id="", + account_id="account_id", + ) diff --git a/tests/api_resources/url_scanner/test_scans.py b/tests/api_resources/url_scanner/test_scans.py new file mode 100644 index 00000000000..933d352b902 --- /dev/null +++ b/tests/api_resources/url_scanner/test_scans.py @@ -0,0 +1,777 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import httpx +import pytest +from respx import MockRouter + +from cloudflare import Cloudflare, AsyncCloudflare +from tests.utils import assert_matches_type +from cloudflare._response import ( + BinaryAPIResponse, + AsyncBinaryAPIResponse, + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, +) +from cloudflare.types.url_scanner import ( + ScanGetResponse, + ScanHARResponse, + ScanListResponse, + ScanBulkCreateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestScans: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Cloudflare) -> None: + scan = client.url_scanner.scans.create( + account_id="account_id", + url="https://www.example.com", + ) + assert_matches_type(str, scan, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Cloudflare) -> None: + scan = client.url_scanner.scans.create( + account_id="account_id", + url="https://www.example.com", + customagent="customagent", + custom_headers={"foo": "string"}, + referer="referer", + screenshots_resolutions=["desktop"], + visibility="Public", + ) + assert_matches_type(str, scan, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Cloudflare) -> None: + response = client.url_scanner.scans.with_raw_response.create( + account_id="account_id", + url="https://www.example.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = response.parse() + assert_matches_type(str, scan, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Cloudflare) -> None: + with client.url_scanner.scans.with_streaming_response.create( + account_id="account_id", + url="https://www.example.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = response.parse() + assert_matches_type(str, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.url_scanner.scans.with_raw_response.create( + account_id="", + url="https://www.example.com", + ) + + @parametrize + def test_method_list(self, client: Cloudflare) -> None: + scan = client.url_scanner.scans.list( + account_id="account_id", + ) + assert_matches_type(ScanListResponse, scan, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Cloudflare) -> None: + scan = client.url_scanner.scans.list( + account_id="account_id", + q="q", + size=100, + ) + assert_matches_type(ScanListResponse, scan, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Cloudflare) -> None: + response = client.url_scanner.scans.with_raw_response.list( + account_id="account_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = response.parse() + assert_matches_type(ScanListResponse, scan, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Cloudflare) -> None: + with client.url_scanner.scans.with_streaming_response.list( + account_id="account_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = response.parse() + assert_matches_type(ScanListResponse, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.url_scanner.scans.with_raw_response.list( + account_id="", + ) + + @parametrize + def test_method_bulk_create(self, client: Cloudflare) -> None: + scan = client.url_scanner.scans.bulk_create( + account_id="account_id", + body=[{"url": "https://www.example.com"}], + ) + assert_matches_type(ScanBulkCreateResponse, scan, path=["response"]) + + @parametrize + def test_raw_response_bulk_create(self, client: Cloudflare) -> None: + response = client.url_scanner.scans.with_raw_response.bulk_create( + account_id="account_id", + body=[{"url": "https://www.example.com"}], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = response.parse() + assert_matches_type(ScanBulkCreateResponse, scan, path=["response"]) + + @parametrize + def test_streaming_response_bulk_create(self, client: Cloudflare) -> None: + with client.url_scanner.scans.with_streaming_response.bulk_create( + account_id="account_id", + body=[{"url": "https://www.example.com"}], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = response.parse() + assert_matches_type(ScanBulkCreateResponse, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_bulk_create(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.url_scanner.scans.with_raw_response.bulk_create( + account_id="", + body=[{"url": "https://www.example.com"}], + ) + + @parametrize + def test_method_dom(self, client: Cloudflare) -> None: + scan = client.url_scanner.scans.dom( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + assert_matches_type(str, scan, path=["response"]) + + @parametrize + def test_raw_response_dom(self, client: Cloudflare) -> None: + response = client.url_scanner.scans.with_raw_response.dom( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = response.parse() + assert_matches_type(str, scan, path=["response"]) + + @parametrize + def test_streaming_response_dom(self, client: Cloudflare) -> None: + with client.url_scanner.scans.with_streaming_response.dom( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = response.parse() + assert_matches_type(str, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_dom(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.url_scanner.scans.with_raw_response.dom( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"): + client.url_scanner.scans.with_raw_response.dom( + scan_id="", + account_id="account_id", + ) + + @parametrize + def test_method_get(self, client: Cloudflare) -> None: + scan = client.url_scanner.scans.get( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + assert_matches_type(ScanGetResponse, scan, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Cloudflare) -> None: + response = client.url_scanner.scans.with_raw_response.get( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = response.parse() + assert_matches_type(ScanGetResponse, scan, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Cloudflare) -> None: + with client.url_scanner.scans.with_streaming_response.get( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = response.parse() + assert_matches_type(ScanGetResponse, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_get(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.url_scanner.scans.with_raw_response.get( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"): + client.url_scanner.scans.with_raw_response.get( + scan_id="", + account_id="account_id", + ) + + @parametrize + def test_method_har(self, client: Cloudflare) -> None: + scan = client.url_scanner.scans.har( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + assert_matches_type(ScanHARResponse, scan, path=["response"]) + + @parametrize + def test_raw_response_har(self, client: Cloudflare) -> None: + response = client.url_scanner.scans.with_raw_response.har( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = response.parse() + assert_matches_type(ScanHARResponse, scan, path=["response"]) + + @parametrize + def test_streaming_response_har(self, client: Cloudflare) -> None: + with client.url_scanner.scans.with_streaming_response.har( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = response.parse() + assert_matches_type(ScanHARResponse, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_har(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.url_scanner.scans.with_raw_response.har( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"): + client.url_scanner.scans.with_raw_response.har( + scan_id="", + account_id="account_id", + ) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_screenshot(self, client: Cloudflare, respx_mock: MockRouter) -> None: + respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + scan = client.url_scanner.scans.screenshot( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + assert scan.is_closed + assert scan.json() == {"foo": "bar"} + assert cast(Any, scan.is_closed) is True + assert isinstance(scan, BinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_screenshot_with_all_params(self, client: Cloudflare, respx_mock: MockRouter) -> None: + respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + scan = client.url_scanner.scans.screenshot( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + resolution="desktop", + ) + assert scan.is_closed + assert scan.json() == {"foo": "bar"} + assert cast(Any, scan.is_closed) is True + assert isinstance(scan, BinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_raw_response_screenshot(self, client: Cloudflare, respx_mock: MockRouter) -> None: + respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + + scan = client.url_scanner.scans.with_raw_response.screenshot( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + + assert scan.is_closed is True + assert scan.http_request.headers.get("X-Stainless-Lang") == "python" + assert scan.json() == {"foo": "bar"} + assert isinstance(scan, BinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_streaming_response_screenshot(self, client: Cloudflare, respx_mock: MockRouter) -> None: + respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + with client.url_scanner.scans.with_streaming_response.screenshot( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) as scan: + assert not scan.is_closed + assert scan.http_request.headers.get("X-Stainless-Lang") == "python" + + assert scan.json() == {"foo": "bar"} + assert cast(Any, scan.is_closed) is True + assert isinstance(scan, StreamedBinaryAPIResponse) + + assert cast(Any, scan.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_path_params_screenshot(self, client: Cloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + client.url_scanner.scans.with_raw_response.screenshot( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"): + client.url_scanner.scans.with_raw_response.screenshot( + scan_id="", + account_id="account_id", + ) + + +class TestAsyncScans: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + async def test_method_create(self, async_client: AsyncCloudflare) -> None: + scan = await async_client.url_scanner.scans.create( + account_id="account_id", + url="https://www.example.com", + ) + assert_matches_type(str, scan, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncCloudflare) -> None: + scan = await async_client.url_scanner.scans.create( + account_id="account_id", + url="https://www.example.com", + customagent="customagent", + custom_headers={"foo": "string"}, + referer="referer", + screenshots_resolutions=["desktop"], + visibility="Public", + ) + assert_matches_type(str, scan, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None: + response = await async_client.url_scanner.scans.with_raw_response.create( + account_id="account_id", + url="https://www.example.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = await response.parse() + assert_matches_type(str, scan, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncCloudflare) -> None: + async with async_client.url_scanner.scans.with_streaming_response.create( + account_id="account_id", + url="https://www.example.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = await response.parse() + assert_matches_type(str, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.url_scanner.scans.with_raw_response.create( + account_id="", + url="https://www.example.com", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncCloudflare) -> None: + scan = await async_client.url_scanner.scans.list( + account_id="account_id", + ) + assert_matches_type(ScanListResponse, scan, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None: + scan = await async_client.url_scanner.scans.list( + account_id="account_id", + q="q", + size=100, + ) + assert_matches_type(ScanListResponse, scan, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None: + response = await async_client.url_scanner.scans.with_raw_response.list( + account_id="account_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = await response.parse() + assert_matches_type(ScanListResponse, scan, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncCloudflare) -> None: + async with async_client.url_scanner.scans.with_streaming_response.list( + account_id="account_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = await response.parse() + assert_matches_type(ScanListResponse, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.url_scanner.scans.with_raw_response.list( + account_id="", + ) + + @parametrize + async def test_method_bulk_create(self, async_client: AsyncCloudflare) -> None: + scan = await async_client.url_scanner.scans.bulk_create( + account_id="account_id", + body=[{"url": "https://www.example.com"}], + ) + assert_matches_type(ScanBulkCreateResponse, scan, path=["response"]) + + @parametrize + async def test_raw_response_bulk_create(self, async_client: AsyncCloudflare) -> None: + response = await async_client.url_scanner.scans.with_raw_response.bulk_create( + account_id="account_id", + body=[{"url": "https://www.example.com"}], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = await response.parse() + assert_matches_type(ScanBulkCreateResponse, scan, path=["response"]) + + @parametrize + async def test_streaming_response_bulk_create(self, async_client: AsyncCloudflare) -> None: + async with async_client.url_scanner.scans.with_streaming_response.bulk_create( + account_id="account_id", + body=[{"url": "https://www.example.com"}], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = await response.parse() + assert_matches_type(ScanBulkCreateResponse, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_bulk_create(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.url_scanner.scans.with_raw_response.bulk_create( + account_id="", + body=[{"url": "https://www.example.com"}], + ) + + @parametrize + async def test_method_dom(self, async_client: AsyncCloudflare) -> None: + scan = await async_client.url_scanner.scans.dom( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + assert_matches_type(str, scan, path=["response"]) + + @parametrize + async def test_raw_response_dom(self, async_client: AsyncCloudflare) -> None: + response = await async_client.url_scanner.scans.with_raw_response.dom( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = await response.parse() + assert_matches_type(str, scan, path=["response"]) + + @parametrize + async def test_streaming_response_dom(self, async_client: AsyncCloudflare) -> None: + async with async_client.url_scanner.scans.with_streaming_response.dom( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = await response.parse() + assert_matches_type(str, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_dom(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.url_scanner.scans.with_raw_response.dom( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"): + await async_client.url_scanner.scans.with_raw_response.dom( + scan_id="", + account_id="account_id", + ) + + @parametrize + async def test_method_get(self, async_client: AsyncCloudflare) -> None: + scan = await async_client.url_scanner.scans.get( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + assert_matches_type(ScanGetResponse, scan, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None: + response = await async_client.url_scanner.scans.with_raw_response.get( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = await response.parse() + assert_matches_type(ScanGetResponse, scan, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None: + async with async_client.url_scanner.scans.with_streaming_response.get( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = await response.parse() + assert_matches_type(ScanGetResponse, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_get(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.url_scanner.scans.with_raw_response.get( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"): + await async_client.url_scanner.scans.with_raw_response.get( + scan_id="", + account_id="account_id", + ) + + @parametrize + async def test_method_har(self, async_client: AsyncCloudflare) -> None: + scan = await async_client.url_scanner.scans.har( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + assert_matches_type(ScanHARResponse, scan, path=["response"]) + + @parametrize + async def test_raw_response_har(self, async_client: AsyncCloudflare) -> None: + response = await async_client.url_scanner.scans.with_raw_response.har( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scan = await response.parse() + assert_matches_type(ScanHARResponse, scan, path=["response"]) + + @parametrize + async def test_streaming_response_har(self, async_client: AsyncCloudflare) -> None: + async with async_client.url_scanner.scans.with_streaming_response.har( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scan = await response.parse() + assert_matches_type(ScanHARResponse, scan, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_har(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.url_scanner.scans.with_raw_response.har( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"): + await async_client.url_scanner.scans.with_raw_response.har( + scan_id="", + account_id="account_id", + ) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_screenshot(self, async_client: AsyncCloudflare, respx_mock: MockRouter) -> None: + respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + scan = await async_client.url_scanner.scans.screenshot( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + assert scan.is_closed + assert await scan.json() == {"foo": "bar"} + assert cast(Any, scan.is_closed) is True + assert isinstance(scan, AsyncBinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_screenshot_with_all_params( + self, async_client: AsyncCloudflare, respx_mock: MockRouter + ) -> None: + respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + scan = await async_client.url_scanner.scans.screenshot( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + resolution="desktop", + ) + assert scan.is_closed + assert await scan.json() == {"foo": "bar"} + assert cast(Any, scan.is_closed) is True + assert isinstance(scan, AsyncBinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_screenshot(self, async_client: AsyncCloudflare, respx_mock: MockRouter) -> None: + respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + + scan = await async_client.url_scanner.scans.with_raw_response.screenshot( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) + + assert scan.is_closed is True + assert scan.http_request.headers.get("X-Stainless-Lang") == "python" + assert await scan.json() == {"foo": "bar"} + assert isinstance(scan, AsyncBinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response_screenshot(self, async_client: AsyncCloudflare, respx_mock: MockRouter) -> None: + respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + async with async_client.url_scanner.scans.with_streaming_response.screenshot( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="account_id", + ) as scan: + assert not scan.is_closed + assert scan.http_request.headers.get("X-Stainless-Lang") == "python" + + assert await scan.json() == {"foo": "bar"} + assert cast(Any, scan.is_closed) is True + assert isinstance(scan, AsyncStreamedBinaryAPIResponse) + + assert cast(Any, scan.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_path_params_screenshot(self, async_client: AsyncCloudflare) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"): + await async_client.url_scanner.scans.with_raw_response.screenshot( + scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + account_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"): + await async_client.url_scanner.scans.with_raw_response.screenshot( + scan_id="", + account_id="account_id", + )