Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@ An easy-to-use and lightweight API wrapper for Censys APIs ([censys.io](https://

> **Notice:** The Censys Search v1 endpoints are deprecated as of Nov. 30, 2021. Please begin using v2 endpoints to query hosts and certificates and check out our [support center](https://support.censys.io/hc/en-us/sections/360013076551-Censys-Search-2-0) for resources.

> [!IMPORTANT]
> This library does not support the new Censys Platform, however a new set of SDKs that do support the platform are coming soon.
> Please refer to the [platform API refrence docs](https://docs.censys.com/reference/get-started#/) in the mean time.

## Features

- [Search Censys data](https://censys-python.readthedocs.io/en/stable/usage-v2.html)
- [Bulk Certificate lookups](https://censys-python.readthedocs.io/en/stable/usage-v2.html#bulk-view)
- [Download Bulk Data](https://censys-python.readthedocs.io/en/stable/usage-v1.html#data)
- [Manage assets, events, and seeds in Censys ASM](https://censys-python.readthedocs.io/en/stable/usage-asm.html)
- [Platform API](https://censys-python.readthedocs.io/en/stable/usage-platform.html)
- [Legacy Search API](https://censys-python.readthedocs.io/en/stable/usage-v2.html)
- [Helpful utilities like Bulk Certificate lookups](https://censys-python.readthedocs.io/en/stable/usage-v2.html#bulk-view)
- [Data Download API](https://censys-python.readthedocs.io/en/stable/usage-v1.html#data)
- [Censys ASM API](https://censys-python.readthedocs.io/en/stable/usage-asm.html)
- [Command-line interface](https://censys-python.readthedocs.io/en/stable/cli.html)

<!-- markdownlint-disable MD033 -->
Expand Down Expand Up @@ -59,7 +56,17 @@ Optionally, you can enable tab completion for the CLI by adding this line to you
eval "$(register-python-argcomplete censys)"
```

To configure your search credentials run `censys config` or set both `CENSYS_API_ID` and `CENSYS_API_SECRET` environment variables.
To configure the platform API, run `censys platform config` or set both `CENSYS_PLATFORM_TOKEN` and `CENSYS_ORGANIZATION_ID` environment variables.

```sh
$ censys platform config

Censys Platform Token: XXX
Censys Organization ID: XXX
Do you want color output? [y/n]: y
```

If you wish to use the legacy Censys Search API, you can configure your search credentials run `censys config` or set both `CENSYS_API_ID` and `CENSYS_API_SECRET` environment variables.

```sh
$ censys config
Expand Down Expand Up @@ -99,6 +106,9 @@ The examples located in the [`examples/`](examples/) directory are a great place
- [Discussions](https://github.com/censys/censys-python/discussions)
- [Censys Homepage](https://censys.io/)
- [Censys Search](https://search.censys.io/)
- [Censys Search API](https://search.censys.io/api)
- [Censys Platform](https://platform.censys.io/)
- [Censys Platform API](https://docs.censys.io/reference/get-started#/)

## Contributing

Expand Down
64 changes: 64 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# https://taskfile.dev

version: "3"

vars:
PYTHON: python
TEST_ARGS: ""

tasks:
default:
deps:
- install
silent: true

install:
desc: Install project dependencies
cmds:
- poetry install
silent: true

test:
desc: Run all tests
cmds:
- poetry run pytest {{.TEST_ARGS}}
silent: true

test:e2e:
desc: Run end-to-end tests only
cmds:
- poetry run pytest -v -m e2e --run-e2e {{.TEST_ARGS}}
silent: true

test:unit:
desc: Run unit tests only (exclude e2e tests)
cmds:
- poetry run pytest -v -m 'not e2e' {{.TEST_ARGS}}
silent: true

lint:
desc: Run linters (black, flake8, etc)
cmds:
- poetry run black .
- poetry run flake8
silent: true

format:
desc: Format code with black
cmds:
- poetry run black .
silent: true

examples:
desc: Run examples with platform credentials
cmds:
- poetry run python examples/platform_search_demo.py
silent: true

clean:
desc: Clean build artifacts
cmds:
- rm -rf build/ dist/ *.egg-info
- find . -type d -name __pycache__ -exec rm -rf {} +
- find . -type f -name "*.pyc" -delete
silent: true
26 changes: 21 additions & 5 deletions censys/asm/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
from censys.common.base import CensysAPIBase
from censys.common.config import DEFAULT, get_config
from censys.common.exceptions import (
CensysAsmException,
CensysAPIException,
CensysException,
CensysExceptionMapper,
CensysInternalServerErrorException,
)


Expand Down Expand Up @@ -53,10 +54,25 @@ def __init__(self, api_key: Optional[str] = None, **kwargs):

def _get_exception_class( # type: ignore
self, res: Response
) -> Type[CensysAsmException]:
return CensysExceptionMapper.ASM_EXCEPTIONS.get(
res.json().get("errorCode"), CensysAsmException
)
) -> Type[CensysAPIException]:
try:
# First try to get error code from JSON response
json_data = res.json()
error_code = json_data.get("errorCode")
if error_code:
# ASM has its own error codes, use the ASM mapper
return CensysExceptionMapper._get_exception_class(error_code, "asm")

# Handle specific HTTP status codes for ASM
if res.status_code == 500:
return CensysInternalServerErrorException
except (ValueError, KeyError):
# If JSON parsing fails, check status code first
if res.status_code == 500:
return CensysInternalServerErrorException
else: # pragma: no cover
# Otherwise use HTTP status code with ASM mapper
return CensysExceptionMapper._get_exception_class(res.status_code, "asm")

def _get_page(
self,
Expand Down
13 changes: 11 additions & 2 deletions censys/cli/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
"""Censys CLI commands."""

from . import account, asm, config, hnri, search, subdomains, view
from . import account, asm, config, hnri, platform, search, subdomains, view

__all__ = ["account", "asm", "config", "hnri", "search", "subdomains", "view"]
__all__ = [
"account",
"asm",
"config",
"hnri",
"platform",
"search",
"subdomains",
"view",
]
19 changes: 13 additions & 6 deletions censys/cli/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,35 +42,42 @@ def cli_config(_: argparse.Namespace): # pragma: no cover
api_id_prompt = f"{api_id_prompt} [cyan]({redacted_id})[/cyan]"
api_secret_prompt = f"{api_secret_prompt} [cyan]({redacted_secret})[/cyan]"

console.print("[bold]Search API Credentials[/bold]")
api_id = Prompt.ask(api_id_prompt, console=console) or api_id
api_secret = Prompt.ask(api_secret_prompt, console=console) or api_secret

if not (api_id and api_secret):
console.print("Please enter valid credentials")
console.print("Please enter valid Search API credentials")
sys.exit(1)

api_id = api_id.strip()
api_secret = api_secret.strip()

console.print(
"\n[bold]Note:[/bold] For Platform API configuration, please use [cyan]censys platform config[/cyan] command."
)

color = Confirm.ask(
"Do you want color output?", default=True, show_default=False, console=console
"\nDo you want color output?", default=True, show_default=False, console=console
)
config.set(DEFAULT, "color", "auto" if color else "")

try:
# Test Search API credentials
client = CensysSearchAPIv2(api_id, api_secret)
account = client.account()
email = account.get("email")
console.print(f"\nSuccessfully authenticated for {email}")
console.print(f"\nSuccessfully authenticated for Search API: {email}")

# Assumes that login was successfully
# Update config
config.set(DEFAULT, "api_id", api_id)
config.set(DEFAULT, "api_secret", api_secret)

write_config(config)
console.print("Configuration saved successfully")
sys.exit(0)
except CensysUnauthorizedException:
console.print("Failed to authenticate")
console.print("Failed to authenticate with Search API")
sys.exit(1)
except PermissionError as e:
console.print(e)
Expand All @@ -91,6 +98,6 @@ def include(parent_parser: argparse._SubParsersAction, parents: dict):
config_parser = parent_parser.add_parser(
"config",
description="Configure Censys Search API Settings",
help="configure Censys search API settings",
help="configure Censys Search API settings",
)
config_parser.set_defaults(func=cli_config)
Loading
Loading