diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..6757794 --- /dev/null +++ b/client/README.md @@ -0,0 +1,39 @@ +# PyLookyloo + +This is the client API for [Lookyloo](https://github.com/CIRCL/lookyloo). + +## Installation + +```bash +pip install pylookyloo +``` + +## Usage + +* You can use the lookyloo command to enqueue an URL. + +```bash +usage: lookyloo [-h] [--url URL] --query QUERY + +Enqueue a URL on Lookyloo. + +optional arguments: + -h, --help show this help message and exit + --url URL URL of the instance (defaults to https://lookyloo.circl.lu/, + the public instance). + --query QUERY URL to enqueue. + +The response is the permanent URL where you can see the result of the capture. +``` + +* Or as a library + +```python + +from pylookyloo import Lookyloo + +lookyloo = Lookyloo('https://url.of.lookyloo.instance') +if lookyloo.is_up(): # to make sure it is up and reachable + permaurl = lookyloo.enqueue('http://url.to.lookup') + +``` diff --git a/client/bin/lookyloo b/client/bin/lookyloo deleted file mode 100755 index 318a948..0000000 --- a/client/bin/lookyloo +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import argparse -from pylookyloo import Lookyloo - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Enqueue a URL on Lookyloo') - parser.add_argument('--url', type=str, help='URL of the instance.') - parser.add_argument('--query', required=True, help='URL to unqueue') - args = parser.parse_args() - - if args.url: - lookyloo = Lookyloo(args.url) - else: - lookyloo = Lookyloo() - - url = lookyloo.enqueue(args.query) - print(url) diff --git a/client/poetry.lock b/client/poetry.lock new file mode 100644 index 0000000..85e709d --- /dev/null +++ b/client/poetry.lock @@ -0,0 +1,80 @@ +[[package]] +category = "main" +description = "Python package for providing Mozilla's CA Bundle." +name = "certifi" +optional = false +python-versions = "*" +version = "2019.11.28" + +[[package]] +category = "main" +description = "Universal encoding detector for Python 2 and 3" +name = "chardet" +optional = false +python-versions = "*" +version = "3.0.4" + +[[package]] +category = "main" +description = "Internationalized Domain Names in Applications (IDNA)" +name = "idna" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.9" + +[[package]] +category = "main" +description = "Python HTTP for Humans." +name = "requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.23.0" + +[package.dependencies] +certifi = ">=2017.4.17" +chardet = ">=3.0.2,<4" +idna = ">=2.5,<3" +urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" + +[package.extras] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] + +[[package]] +category = "main" +description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "1.25.8" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] + +[metadata] +content-hash = "4d17f9e80f90dd84c09b8f46829319f9ba04c4a24359b783035c9f15e84a8115" +python-versions = "^3.6" + +[metadata.files] +certifi = [ + {file = "certifi-2019.11.28-py2.py3-none-any.whl", hash = "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3"}, + {file = "certifi-2019.11.28.tar.gz", hash = "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"}, +] +chardet = [ + {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, + {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, +] +idna = [ + {file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"}, + {file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"}, +] +requests = [ + {file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"}, + {file = "requests-2.23.0.tar.gz", hash = "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"}, +] +urllib3 = [ + {file = "urllib3-1.25.8-py2.py3-none-any.whl", hash = "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc"}, + {file = "urllib3-1.25.8.tar.gz", hash = "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"}, +] diff --git a/client/pylookyloo/__init__.py b/client/pylookyloo/__init__.py index 1f94739..58af1af 100644 --- a/client/pylookyloo/__init__.py +++ b/client/pylookyloo/__init__.py @@ -1 +1,21 @@ from .api import Lookyloo + +import argparse + + +def main(): + parser = argparse.ArgumentParser(description='Enqueue a URL on Lookyloo.', epilog='The response is the permanent URL where you can see the result of the capture.') + parser.add_argument('--url', type=str, help='URL of the instance (defaults to https://lookyloo.circl.lu/, the public instance).') + parser.add_argument('--query', required=True, help='URL to enqueue.') + args = parser.parse_args() + + if args.url: + lookyloo = Lookyloo(args.url) + else: + lookyloo = Lookyloo() + + if lookyloo.is_up(): + url = lookyloo.enqueue(args.query) + print(url) + else: + print(f'Unable to reach {lookyloo.root_url}. Is the server up?') diff --git a/client/pylookyloo/api.py b/client/pylookyloo/api.py index de5b4b4..40b4ac9 100644 --- a/client/pylookyloo/api.py +++ b/client/pylookyloo/api.py @@ -16,10 +16,10 @@ class Lookyloo(): self.session = requests.session() @property - def is_up(self): + def is_up(self) -> bool: r = self.session.head(self.root_url) return r.status_code == 200 - def enqueue(self, url: str): + def enqueue(self, url: str) -> str: response = self.session.post(urljoin(self.root_url, 'submit'), data=json.dumps({'url': url})) return urljoin(self.root_url, f'tree/{response.text}') diff --git a/client/pylookyloo/py.typed b/client/pylookyloo/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/client/pyproject.toml b/client/pyproject.toml index c1b143c..67e6a3a 100644 --- a/client/pyproject.toml +++ b/client/pyproject.toml @@ -1,25 +1,28 @@ [tool.poetry] name = "pylookyloo" -version = "1.0" +version = "0.5" description = "Python client for Lookyloo" authors = ["Raphaël Vinot "] license = "AGPL-3.0-or-later" repository = "https://github.com/CIRCL/lookyloo/client" classifiers = [ - 'Development Status :: 3 - Alpha', + 'Development Status :: 4 - Beta', 'Environment :: Console', 'Operating System :: POSIX :: Linux', 'Intended Audience :: Science/Research', 'Intended Audience :: Telecommunications Industry', 'Intended Audience :: Information Technology', 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Topic :: Security', 'Topic :: Internet', ] [tool.poetry.scripts] -lookyloo = "bin/lookyloo" +lookyloo = 'pylookyloo:main' [tool.poetry.dependencies] python = "^3.6" diff --git a/client/setup.py b/client/setup.py deleted file mode 100644 index a022d46..0000000 --- a/client/setup.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from setuptools import setup - - -setup( - name='pylookyloo', - version='0.1', - author='Raphaël Vinot', - author_email='raphael.vinot@circl.lu', - maintainer='Raphaël Vinot', - url='https://github.com/CIRCL/lookyloo/client', - description='Python client for Lookyloo', - packages=['pylookyloo'], - scripts=['bin/lookyloo'], - install_requires=['requests'], - classifiers=[ - 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)', - 'Development Status :: 3 - Alpha', - 'Environment :: Console', - 'Operating System :: POSIX :: Linux', - 'Intended Audience :: Science/Research', - 'Intended Audience :: Telecommunications Industry', - 'Intended Audience :: Information Technology', - 'Programming Language :: Python :: 3', - 'Topic :: Security', - 'Topic :: Internet', - ] -)