Compare commits
52 commits
Author | SHA1 | Date | |
---|---|---|---|
|
8846414940 | ||
ada7c07eac | |||
|
9183302a1f | ||
bbfe16ed28 | |||
15c165074b | |||
|
036692ba35 | ||
529a90773f | |||
21076b20c7 | |||
d4eb075911 | |||
1a2fab6135 | |||
593b221137 | |||
|
252f74b59e | ||
5f35baebb9 | |||
cc23ddc7c7 | |||
457b36e1da | |||
c22f282a61 | |||
c6d657952d | |||
18a59afead | |||
0ab0fb8000 | |||
6092237594 | |||
|
5455503445 | ||
ddf0614712 | |||
d3ce964ded | |||
978b42ae57 | |||
185915df34 | |||
b1e0864117 | |||
7326894353 | |||
294b2f0ad2 | |||
667e1c174e | |||
f21cf8b100 | |||
a32e116b2a | |||
|
1cdd2746c6 | ||
157da703b7 | |||
61a92c5c63 | |||
96146be161 | |||
5c4171758c | |||
6c5c368e68 | |||
03cadb90cf | |||
|
840044ab28 | ||
498a581626 | |||
ca17b3a871 | |||
971bf6aec7 | |||
ef5756bd95 | |||
310a9f7ffe | |||
cee20e2c7c | |||
|
386a983b79 | ||
dd39b24277 | |||
5b59455bef | |||
0f43276109 | |||
26638e7e73 | |||
9a1a960641 | |||
d15740d3e9 |
25 changed files with 1192 additions and 132 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@
|
|||
db.sqlite3
|
||||
dist
|
||||
.coverage
|
||||
docs/build
|
||||
|
|
|
@ -45,7 +45,7 @@ limit-inference-results=100
|
|||
|
||||
# List of plugins (as comma separated values of python module names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=pylint_django
|
||||
load-plugins=
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
@ -548,7 +548,7 @@ valid-metaclass-classmethod-first-arg=cls
|
|||
ignored-parents=
|
||||
|
||||
# Maximum number of arguments for function / method.
|
||||
max-args=5
|
||||
max-args=50
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
|
101
CHANGELOG.md
101
CHANGELOG.md
|
@ -1,7 +1,106 @@
|
|||
# Changelog
|
||||
|
||||
## Unreleased (2021-09-28)
|
||||
## v0.7.0 (2021-10-07)
|
||||
|
||||
#### New Features
|
||||
|
||||
* (completion): add completion scripts
|
||||
#### Docs
|
||||
|
||||
* add intro readme for docs building
|
||||
* add shell completion information
|
||||
* update full doc url in readme
|
||||
* remove personal "you/I/me"
|
||||
* add full documentation site
|
||||
* update changelog for 0.6.0
|
||||
#### Others
|
||||
|
||||
* fix separator testing issues
|
||||
|
||||
Full set of changes: [`v0.6.0...v0.7.0`](https://git.admin.franklin.edu/tins/checkcert/compare/v0.6.0...v0.7.0)
|
||||
|
||||
## v0.6.0 (2021-10-04)
|
||||
|
||||
#### New Features
|
||||
|
||||
* add separator for --san-only
|
||||
#### Fixes
|
||||
|
||||
* default valid output text to "false", override with "--valid"
|
||||
#### Docs
|
||||
|
||||
* update docs to reflect all current options
|
||||
* add installation section
|
||||
* enable rtd theme
|
||||
* add rough intro to test rtd integration
|
||||
* add docs dir for handling sphinx-based documentation
|
||||
#### Others
|
||||
|
||||
* add docs to make
|
||||
|
||||
Full set of changes: [`v0.5.0...v0.6.0`](https://git.admin.franklin.edu/tins/checkcert/compare/v0.5.0...v0.6.0)
|
||||
|
||||
## v0.5.0 (2021-10-04)
|
||||
|
||||
#### New Features
|
||||
|
||||
* add ability to output just the sans in a space separated list
|
||||
#### Refactorings
|
||||
|
||||
* use Any as a workaround for specific types that haven't been imported yet
|
||||
* add type hints to all functions
|
||||
* cleanup main logic to reduce branches
|
||||
|
||||
Full set of changes: [`v0.4.0...v0.5.0`](https://git.admin.franklin.edu/tins/checkcert/compare/v0.4.0...v0.5.0)
|
||||
|
||||
## v0.4.0 (2021-10-01)
|
||||
|
||||
#### New Features
|
||||
|
||||
* add text output for cert validity in addition to color
|
||||
#### Refactorings
|
||||
|
||||
* align output data rather than heading for output
|
||||
#### Docs
|
||||
|
||||
* update readme with more details
|
||||
#### Others
|
||||
|
||||
* add ci directory for test input data
|
||||
* correct lint error on file encoding
|
||||
* add test for file input
|
||||
|
||||
Full set of changes: [`v0.3.0...v0.4.0`](https://git.admin.franklin.edu/tins/checkcert/compare/v0.3.0...v0.4.0)
|
||||
|
||||
## v0.3.0 (2021-10-01)
|
||||
|
||||
#### New Features
|
||||
|
||||
* add option to get host names from an external file
|
||||
#### Refactorings
|
||||
|
||||
* clean-up for pylint
|
||||
#### Docs
|
||||
|
||||
* update docs with a _little_ more detail
|
||||
#### Others
|
||||
|
||||
* fix spelling
|
||||
* refactor test code for pep8
|
||||
* update tests to handle all branches
|
||||
|
||||
Full set of changes: [`v0.2.0...v0.3.0`](https://git.admin.franklin.edu/tins/checkcert/compare/v0.2.0...v0.3.0)
|
||||
|
||||
## v0.2.0 (2021-09-30)
|
||||
|
||||
#### New Features
|
||||
|
||||
* add color output for validity check of cert
|
||||
* add option to display the text version of the x509 cert
|
||||
#### Fixes
|
||||
|
||||
* remove duplication of output
|
||||
#### Others
|
||||
|
||||
* add base coverage for all functions
|
||||
* add initial test cases
|
||||
|
|
6
Makefile
6
Makefile
|
@ -18,6 +18,10 @@ dir:
|
|||
clean:
|
||||
rm -rf $(BUILDDIR)
|
||||
find . -name __pycache__|xargs rm -rf
|
||||
rm -rf docs/build
|
||||
|
||||
docs:
|
||||
cd docs && make html
|
||||
|
||||
poetry-release: build
|
||||
poetry publish
|
||||
|
@ -49,4 +53,4 @@ tea-release: build
|
|||
|
||||
release: poetry-release
|
||||
|
||||
.PHONY: dir clean release gh-release poetry-release coverage tea-release
|
||||
.PHONY: dir clean release gh-release poetry-release coverage tea-release docs
|
||||
|
|
52
README.md
52
README.md
|
@ -1 +1,51 @@
|
|||
get information about certificates, by default will output the expiration date
|
||||
# checkcert
|
||||
|
||||
This utility was based off of [this
|
||||
gist](https://gist.github.com/gdamjan/55a8b9eec6cf7b771f92021d93b87b2c).
|
||||
|
||||
checkcert has the logic of that gist wrapped in a click-based CLI and added command-line options
|
||||
(checkcert --help to see them)
|
||||
|
||||
Full documentation is available at
|
||||
[https://checkcert.readthedocs.io](https://checkcert.readthedocs.io)
|
||||
|
||||
# Installation
|
||||
|
||||
## from PyPi
|
||||
pip install checkert
|
||||
|
||||
# Usage
|
||||
|
||||
When you run `pip install checkcert`, you will get a `checkcert` command. To
|
||||
show all the options, simply run `checkcert --help` to get the most-current list
|
||||
of commands and options.
|
||||
|
||||
### Basic Usage
|
||||
The basic usage is `checkcert example.com`
|
||||
|
||||
### Check cert with an alternate port
|
||||
|
||||
Anywhere you specify the host, you may use the format `host:port` to specify an
|
||||
alternate port. If no port is specified, 443 will be used. To check something
|
||||
running on port 8081 for example, execute `checkcert example.com:8081`
|
||||
|
||||
### Multiple domains
|
||||
|
||||
checkcert will take all domains specified on the command line. Multiple values
|
||||
may be specified as `checkcert example.com www.example.com alt.example.com:444`
|
||||
|
||||
### Domain list from a file
|
||||
|
||||
checkcert can be instructed to pull the list of domains from a file instead with
|
||||
the --filename option. The file contents will just be a domain per line
|
||||
(specified in host:port format, or just host to default to port 443)
|
||||
|
||||
create a file named domains.txt with contents like the following
|
||||
|
||||
```
|
||||
example.com
|
||||
www.example.com
|
||||
alt.example.com:444
|
||||
```
|
||||
|
||||
Then execute `checkcert --filename domains.txt`
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import re
|
||||
import sys
|
||||
from checkcert.checkcert import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -1,91 +1,132 @@
|
|||
# heavily modified version of https://gist.githubusercontent.com/gdamjan/55a8b9eec6cf7b771f92021d93b87b2c/raw/d8dc194ec4d0187f985a57138019d04e3a59b51f/ssl-check.py
|
||||
# to give a CLI for passing the hosts to check and other optional output
|
||||
""" CLI to get basic information about certificates and determine validity"""
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
import concurrent.futures
|
||||
from socket import socket
|
||||
from typing import List, Tuple, Any
|
||||
import click
|
||||
from OpenSSL import SSL
|
||||
from OpenSSL import crypto
|
||||
from OpenSSL.crypto import X509
|
||||
from cryptography import x509
|
||||
from cryptography.x509.oid import NameOID
|
||||
import idna
|
||||
import sys
|
||||
|
||||
from socket import socket
|
||||
from collections import namedtuple
|
||||
|
||||
__version__ = "0.2.0"
|
||||
|
||||
HostInfo = namedtuple(
|
||||
field_names="cert hostname peername is_valid", typename="HostInfo"
|
||||
)
|
||||
|
||||
|
||||
def get_certificate(hostname, port):
|
||||
__version__ = "0.7.2"
|
||||
|
||||
HostInfo = namedtuple("HostInfo", ["cert", "hostname", "peername", "is_valid"])
|
||||
|
||||
|
||||
def get_certificate(hostname: str, port: int) -> HostInfo:
|
||||
"""retrieve certificate details and return HostInfo tuple of values"""
|
||||
hostname_idna = idna.encode(hostname)
|
||||
sock = socket()
|
||||
|
||||
try:
|
||||
sock.connect((hostname, port))
|
||||
peername = sock.getpeername()
|
||||
ctx = SSL.Context(SSL.SSLv23_METHOD) # most compatible
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = SSL.VERIFY_NONE
|
||||
sock_ssl = SSL.Connection(ctx, sock)
|
||||
sock_ssl.set_connect_state()
|
||||
sock_ssl.set_tlsext_host_name(hostname_idna)
|
||||
sock_ssl.do_handshake()
|
||||
cert = sock_ssl.get_peer_certificate()
|
||||
crypto_cert = cert.to_cryptography()
|
||||
sock_ssl.close()
|
||||
sock.close()
|
||||
except ConnectionRefusedError:
|
||||
pass
|
||||
print("Error: Connection Refused")
|
||||
sys.exit(3)
|
||||
peername = sock.getpeername()
|
||||
ctx = SSL.Context(SSL.SSLv23_METHOD) # most compatible
|
||||
sock_ssl = SSL.Connection(ctx, sock)
|
||||
sock_ssl.set_connect_state()
|
||||
sock_ssl.set_tlsext_host_name(hostname_idna)
|
||||
sock_ssl.do_handshake()
|
||||
cert = sock_ssl.get_peer_certificate()
|
||||
crypto_cert = cert.to_cryptography()
|
||||
sock_ssl.close()
|
||||
sock.close()
|
||||
|
||||
return HostInfo(
|
||||
cert=crypto_cert,
|
||||
peername=peername,
|
||||
hostname=hostname,
|
||||
is_valid=not cert.has_expired(),
|
||||
is_valid=not cert.has_expired(), # is_valid is the inverse of has_expired
|
||||
)
|
||||
|
||||
|
||||
def get_alt_names(cert):
|
||||
def get_alt_names(cert: x509.Certificate) -> Any:
|
||||
"""retrieve the SAN values for given cert"""
|
||||
try:
|
||||
ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName)
|
||||
return ext.value.get_values_for_type(x509.DNSName)
|
||||
except x509.ExtensionNotFound:
|
||||
except x509.ExtensionNotFound: # pragma: no cover
|
||||
return None
|
||||
|
||||
|
||||
def get_x509_text(cert):
|
||||
def get_x509_text(cert: X509) -> bytes:
|
||||
"""return the human-readable text version of the certificate"""
|
||||
return crypto.dump_certificate(crypto.FILETYPE_TEXT, cert)
|
||||
|
||||
|
||||
def get_common_name(cert):
|
||||
def get_common_name(cert: x509.Certificate) -> Any:
|
||||
"""Return the common name from the certificate"""
|
||||
try:
|
||||
names = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
|
||||
return names[0].value
|
||||
except x509.ExtensionNotFound:
|
||||
except x509.ExtensionNotFound: # pragma: no cover
|
||||
return None
|
||||
|
||||
|
||||
def get_issuer(cert):
|
||||
def get_issuer(cert: x509.Certificate) -> Any:
|
||||
"""Return the name of the CA/Issuer of the certificate"""
|
||||
try:
|
||||
names = cert.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)
|
||||
return names[0].value
|
||||
except x509.ExtensionNotFound:
|
||||
except x509.ExtensionNotFound: # pragma: no cover
|
||||
return None
|
||||
|
||||
|
||||
def print_basic_info(hostinfo):
|
||||
print(
|
||||
f"""
|
||||
{hostinfo.hostname} ({hostinfo.peername[0]}:{hostinfo.peername[1]})
|
||||
\tcommonName: {get_common_name(hostinfo.cert)}
|
||||
\tSAN: {get_alt_names(hostinfo.cert)}
|
||||
\tissuer: {get_issuer(hostinfo.cert)}
|
||||
\tnotBefore: {hostinfo.cert.not_valid_before}
|
||||
\tnotAfter: {hostinfo.cert.not_valid_after}
|
||||
"""
|
||||
def get_host_list_tuple(hosts: list) -> List[Tuple[str, int]]:
|
||||
"""create a tuple of host and port based on hosts given to us in the form
|
||||
host:port
|
||||
"""
|
||||
all_hosts = []
|
||||
for host in hosts:
|
||||
# if a host has a : in it, split on the :, first field will be host
|
||||
# second field will be the port
|
||||
if ":" in host:
|
||||
host_info = host.split(":")
|
||||
all_hosts.append((host_info[0], int(host_info[1])))
|
||||
else:
|
||||
all_hosts.append((host, 443))
|
||||
return all_hosts
|
||||
|
||||
|
||||
def print_output(hostinfo: HostInfo, settings: dict) -> None:
|
||||
"""print the output of hostinfo conditionally based on items in settings"""
|
||||
output_string: str = ""
|
||||
if settings["san_only"]:
|
||||
if settings["pre"]:
|
||||
output_string += f"{settings['sep']}".lstrip()
|
||||
alt_names = get_alt_names(hostinfo.cert)
|
||||
if hostinfo.hostname not in alt_names:
|
||||
alt_names.insert(0, hostinfo.hostname)
|
||||
output_string += f"{settings['sep']}".join(alt_names)
|
||||
print(output_string)
|
||||
# in the san-only branch, once we get to this point the output is
|
||||
# already complete, so just return to end the function processing
|
||||
return None
|
||||
output_string += (
|
||||
f"\n{hostinfo.hostname} " f"({hostinfo.peername[0]}:{hostinfo.peername[1]})\n"
|
||||
)
|
||||
output_string += f" commonName: {get_common_name(hostinfo.cert)}\n"
|
||||
output_string += f" issuer: {get_issuer(hostinfo.cert)}\n"
|
||||
output_string += f" notBefore: {hostinfo.cert.not_valid_before}\n"
|
||||
output_string += f" notAfter: {hostinfo.cert.not_valid_after}\n"
|
||||
if settings["valid"]:
|
||||
output_string += f" Valid: {hostinfo.is_valid}\n"
|
||||
if settings["san"]:
|
||||
output_string += f" SAN: {get_alt_names(hostinfo.cert)}\n"
|
||||
if hostinfo.is_valid and settings["color"]:
|
||||
click.echo(click.style(output_string, fg="green"))
|
||||
elif not hostinfo.is_valid and settings["color"]:
|
||||
click.echo(click.style(output_string, fg="red"))
|
||||
else:
|
||||
click.echo(click.style(output_string))
|
||||
return None
|
||||
|
||||
|
||||
@click.command()
|
||||
|
@ -94,43 +135,58 @@ def print_basic_info(hostinfo):
|
|||
@click.option(
|
||||
"--dump", is_flag=True, help="Dump the full text version of the x509 certificate"
|
||||
)
|
||||
@click.option("--color/--no-color", default=True)
|
||||
@click.option(
|
||||
"--color/--no-color",
|
||||
default=True,
|
||||
help="Enable/disable ANSI color output to show cert validity",
|
||||
)
|
||||
@click.option(
|
||||
"--filename",
|
||||
"-f",
|
||||
type=click.Path(),
|
||||
help="Read a list of hosts to check from a file",
|
||||
)
|
||||
@click.option(
|
||||
"--valid/--no-valid", default=False, help="Show True/False for cert validity"
|
||||
)
|
||||
@click.option(
|
||||
"--san-only",
|
||||
"-o",
|
||||
is_flag=True,
|
||||
help="Output only the SAN names to use in passing to certbot for example",
|
||||
)
|
||||
@click.option(
|
||||
"--pre/--no-pre", default=False, help="prefix the --san-only with the separator"
|
||||
)
|
||||
@click.option("--sep", "-s", default=" ", help="Separator to use in --san-only output")
|
||||
@click.argument("hosts", nargs=-1)
|
||||
def main(san, dump, color, hosts):
|
||||
def main(san, dump, color, filename, valid, san_only, sep, pre, hosts):
|
||||
"""Return information about certificates given including their validity"""
|
||||
# setup the list of tuples
|
||||
HOSTS = []
|
||||
# handle a domain given with a : in it to specify the port
|
||||
for host in hosts:
|
||||
# if a host has a : in it, split on the :, first field will be host
|
||||
# second field will be the port
|
||||
if ":" in host:
|
||||
host_info = host.split(":")
|
||||
HOSTS.append((host_info[0], int(host_info[1])))
|
||||
else:
|
||||
HOSTS.append((host, 443))
|
||||
for hostinfo in map(lambda x: get_certificate(x[0], x[1]), HOSTS):
|
||||
output_string = ""
|
||||
if dump:
|
||||
print(get_x509_text(hostinfo.cert).decode())
|
||||
else:
|
||||
output_string += (
|
||||
f"{hostinfo.hostname} ({hostinfo.peername[0]}:{hostinfo.peername[1]})\n"
|
||||
)
|
||||
output_string += f"\tcommonName: {get_common_name(hostinfo.cert)}\n"
|
||||
if san:
|
||||
output_string += f"\tSAN: {get_alt_names(hostinfo.cert)}\n"
|
||||
output_string += f"\tissuer: {get_issuer(hostinfo.cert)}\n"
|
||||
output_string += f"\tnotBefore: {hostinfo.cert.not_valid_before}\n"
|
||||
output_string += f"\tnotAfter: {hostinfo.cert.not_valid_after}\n\n"
|
||||
if hostinfo.is_valid and color:
|
||||
click.echo(click.style(output_string, fg="green"))
|
||||
elif not hostinfo.is_valid and color:
|
||||
click.echo(click.style(output_string, fg="red"))
|
||||
if filename:
|
||||
hosts = []
|
||||
with open(filename, "r", encoding="utf-8") as infile:
|
||||
for line in infile:
|
||||
line = line.strip()
|
||||
hosts.append(line)
|
||||
all_hosts = get_host_list_tuple(hosts)
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as epool:
|
||||
for hostinfo in epool.map(lambda x: get_certificate(x[0], x[1]), all_hosts):
|
||||
if dump:
|
||||
print(get_x509_text(hostinfo.cert).decode())
|
||||
else:
|
||||
click.echo(click.style(output_string))
|
||||
|
||||
# print(f"Certificate for {domain}\nexpires after: {x509.get_not_after()}")
|
||||
settings_dict = {
|
||||
"san": san,
|
||||
"sep": sep,
|
||||
"dump": dump,
|
||||
"color": color,
|
||||
"valid": valid,
|
||||
"san_only": san_only,
|
||||
"pre": pre,
|
||||
}
|
||||
print_output(hostinfo, settings_dict)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main() # pragma: no cover
|
||||
main() # pylint: disable=no-value-for-parameter # pragma: no cover
|
||||
|
|
3
ci/test_domains.txt
Normal file
3
ci/test_domains.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
example.com
|
||||
www.example.com
|
||||
example.net
|
29
completions/checkcert-complete.sh
Normal file
29
completions/checkcert-complete.sh
Normal file
|
@ -0,0 +1,29 @@
|
|||
_checkcert_completion() {
|
||||
local IFS=$'\n'
|
||||
local response
|
||||
|
||||
response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD _CHECKCERT_COMPLETE=bash_complete $1)
|
||||
|
||||
for completion in $response; do
|
||||
IFS=',' read type value <<< "$completion"
|
||||
|
||||
if [[ $type == 'dir' ]]; then
|
||||
COMREPLY=()
|
||||
compopt -o dirnames
|
||||
elif [[ $type == 'file' ]]; then
|
||||
COMREPLY=()
|
||||
compopt -o default
|
||||
elif [[ $type == 'plain' ]]; then
|
||||
COMPREPLY+=($value)
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_checkcert_completion_setup() {
|
||||
complete -o nosort -F _checkcert_completion checkcert
|
||||
}
|
||||
|
||||
_checkcert_completion_setup;
|
||||
|
35
completions/checkcert-complete.zsh
Normal file
35
completions/checkcert-complete.zsh
Normal file
|
@ -0,0 +1,35 @@
|
|||
#compdef checkcert
|
||||
|
||||
_checkcert_completion() {
|
||||
local -a completions
|
||||
local -a completions_with_descriptions
|
||||
local -a response
|
||||
(( ! $+commands[checkcert] )) && return 1
|
||||
|
||||
response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) _CHECKCERT_COMPLETE=zsh_complete checkcert)}")
|
||||
|
||||
for type key descr in ${response}; do
|
||||
if [[ "$type" == "plain" ]]; then
|
||||
if [[ "$descr" == "_" ]]; then
|
||||
completions+=("$key")
|
||||
else
|
||||
completions_with_descriptions+=("$key":"$descr")
|
||||
fi
|
||||
elif [[ "$type" == "dir" ]]; then
|
||||
_path_files -/
|
||||
elif [[ "$type" == "file" ]]; then
|
||||
_path_files -f
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$completions_with_descriptions" ]; then
|
||||
_describe -V unsorted completions_with_descriptions -U
|
||||
fi
|
||||
|
||||
if [ -n "$completions" ]; then
|
||||
compadd -U -V unsorted -a completions
|
||||
fi
|
||||
}
|
||||
|
||||
compdef _checkcert_completion checkcert;
|
||||
|
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
4
docs/README.md
Normal file
4
docs/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# build pdf docs
|
||||
If you want to build the pdf documentation via ``make latexpdf`` be warned that it requires a lot of packages. For fedora, the following should get all the dependencies to make it work:
|
||||
|
||||
``dnf -y install texlive-gsftop texlive-makeindexk texlive-parskip texlive-tabulary texlive-needspace texlive-upquote texlive-fancyvrb texlive-framed texlive-capt-of texlive-wrapfig texlive-float texlive-fncychap texlive-tex-gyre texlive-babel texlive-amsfonts texlive-amsmath texlive-ec texlive-cmap latexmk``
|
57
docs/source/conf.py
Normal file
57
docs/source/conf.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
import sphinx_rtd_theme
|
||||
|
||||
project = "checkcert"
|
||||
copyright = "2021, Alex Kelly"
|
||||
author = "Alex Kelly"
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = "0.5.0"
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
"sphinx_rtd_theme",
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = []
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ["_static"]
|
BIN
docs/source/images/basic.png
Normal file
BIN
docs/source/images/basic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
BIN
docs/source/images/multi.png
Normal file
BIN
docs/source/images/multi.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 74 KiB |
BIN
docs/source/images/san.png
Normal file
BIN
docs/source/images/san.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
BIN
docs/source/images/valid.png
Normal file
BIN
docs/source/images/valid.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
16
docs/source/index.rst
Normal file
16
docs/source/index.rst
Normal file
|
@ -0,0 +1,16 @@
|
|||
.. checkcert documentation master file, created by
|
||||
sphinx-quickstart on Mon Oct 4 12:00:40 2021.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to checkcert's documentation!
|
||||
=====================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
introduction
|
||||
install
|
||||
running
|
||||
shell_completion
|
26
docs/source/install.rst
Normal file
26
docs/source/install.rst
Normal file
|
@ -0,0 +1,26 @@
|
|||
Installation
|
||||
============
|
||||
|
||||
From Pypi
|
||||
---------
|
||||
|
||||
The easiest way is to install via pypi via:
|
||||
|
||||
``pip install checkcert``
|
||||
|
||||
If you already have checkcert installed and wish to update it, run:
|
||||
|
||||
``pip install -U checkcert``
|
||||
|
||||
This will upgrade the currently-installed version to the latest.
|
||||
|
||||
From Git
|
||||
--------
|
||||
|
||||
There are many methods for running within a virtual environment isolated for a particular package. Certcheck has been built with poetry, so this document details that method, but there are certainly other ways to accomplish the same thing
|
||||
|
||||
1. clone the repo ``git clone https://github.com/kellya/checkcert.git``
|
||||
2. cd to the directory you just cloned ``cd checkcert``
|
||||
3. install the required packages ``poetry install``
|
||||
4. activate the poetry-built venv ``poetry shell``
|
||||
5. run with ``python certcheck/certcheck.py``
|
4
docs/source/introduction.rst
Normal file
4
docs/source/introduction.rst
Normal file
|
@ -0,0 +1,4 @@
|
|||
Introduction
|
||||
============
|
||||
|
||||
checkcert is a CLI to check a remote site secured with a certificate. It will allow you to get basic information about the certificate, if it is currently valid, any subjectAlternateNames that are present, and output content for ingestion into other utilities
|
214
docs/source/running.rst
Normal file
214
docs/source/running.rst
Normal file
|
@ -0,0 +1,214 @@
|
|||
Running checkcert
|
||||
=================
|
||||
|
||||
This section assumes that checkcert is installed via pypi, and the ``certcheck`` entry-point in the path (which ``pip install checkcert`` should do automatically). If running from source, the full path to python and the checkcert.py script itself would need to be specified.
|
||||
|
||||
Basic query
|
||||
-----------
|
||||
|
||||
To do a simple validation, run something like ``checkcert www.example.com``. This should yield output like:
|
||||
|
||||
.. figure:: images/basic.png
|
||||
:alt: basic output example
|
||||
|
||||
Basic command output
|
||||
|
||||
|
||||
The green text is showing that the certificate is valid. If the certificate had expired, the text would be displayed in Red. The ``--valid`` option may be specified to display a text represention of the cert validity in addtion to just coloring the output to show validity. When run with ``--valid``, the output will look like:
|
||||
|
||||
.. figure:: images/valid.png
|
||||
:alt: output with valid option
|
||||
|
||||
Display cert validity in output text
|
||||
|
||||
|
||||
.. note:: Output color is generated via Click's styling which should be aware of how the text is being handled and appropriately strip out the color sequences if output is being piped to another command, for example. The plain text output may be enforced with the ``--no-color`` option
|
||||
|
||||
Querying multiple domains
|
||||
-------------------------
|
||||
|
||||
Multiple domains can be checked from a single command. Simply separate them on the command line with a space. So to check `www.example.com` and `www.example.org`, this can be run as:
|
||||
|
||||
``checkcert www.example.com www.example.org``
|
||||
|
||||
Which will generate:
|
||||
|
||||
.. figure:: images/multi.png
|
||||
:alt: multi-domain output
|
||||
|
||||
Mutliple domains specified
|
||||
|
||||
Specify alternate ports
|
||||
-----------------------
|
||||
|
||||
By default, the standard port `443` will be used. If a service is running on an alternate port, simply append `:portnum` to the domain. So if example.com was listening on 444 instead of 443, anywhere the domain is specified, the format ``example.com:444`` may be used instead.
|
||||
|
||||
Domains from a file
|
||||
-------------------
|
||||
|
||||
While specifying a few domains on the commanline is fine, if there are many domains to check, an input file may be specified instead. Each domain should be on its own line. Alternate ports may be specified in this file too. For example, if a file named `domains.txt` is created with the following content:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
www.example.com
|
||||
example.org
|
||||
example.com:444
|
||||
|
||||
Then checkcert may be run as follows:
|
||||
|
||||
``checkcert --filename domains.txt``
|
||||
|
||||
Each line in the file will be parsed and checked the same as if it were run on the command line.
|
||||
|
||||
SubjectAlternateName handling
|
||||
-----------------------------
|
||||
|
||||
checkcert has a `--san` option that will optionally show the subjectAlternateNames for a domain. This will output the same as previous iterations, but will add a list of SANs associated with the certificate retrieved.
|
||||
|
||||
.. figure:: images/san.png
|
||||
:alt: subjectalternatename example
|
||||
|
||||
output with the SAN line
|
||||
|
||||
|
||||
Just the SANs
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
It may be useful to output just the SANs for a given domain. This may be done by specifying the ``--san-only`` option. By default this will output all of SANs, separated by a space.
|
||||
|
||||
Running ``checkcert --san-only www.example.com`` will yield::
|
||||
|
||||
www.example.org example.com example.edu example.net example.org www.example.com www.example.edu www.example.net
|
||||
|
||||
If an alternate separator is desired, add the ``--sep <separator>`` option, so for comma separated, run ``checkcert --san-only --sep , www.example.com`` which will yield::
|
||||
|
||||
www.example.org,example.com,example.edu,example.net,example.org,www.example.com,www.example.edu,www.example.net
|
||||
|
||||
If it is desired to output that list to another command as commandline options (for example when migrating from a managed PKI front-end to using ACME/certbot). Certbot wants the domains specified as -d. checkcert may run as: ``checkcert --san-only --sep " -d "- www.example.com`` which will return::
|
||||
|
||||
www.example.org -d example.com -d example.edu -d example.net -d example.org -d www.example.com -d www.example.edu -d www.example.net
|
||||
|
||||
.. note:: the documentation for certbot specifies multiple domains should be specified each with their own '-d' option. certbot in particular seems to work with a comma-separated list too, even though the docs don't specify that it works that way.
|
||||
|
||||
Notice that the " -d " is used as a **separator** and therefore the first entry does not have a -d before it. To make -d show up before every entry (as would be required in this case) The ``--pre`` option must be added to "prefix" the entry with the separator; thus, running ``checkcert --san-only --sep " -d " --pre www.example.com`` will yield::
|
||||
|
||||
-d www.example.org -d example.com -d example.edu -d example.net -d example.org -d www.example.com -d www.example.edu -d www.example.net
|
||||
|
||||
.. note:: Since this is utilizing separator in a slightly different way, the space before the option must be specified, otherwise everything would run together. checkcert is aware of this and will strip off the space at the beginning if ``--pre`` is used.
|
||||
|
||||
This output could be directly included into certbot as ``certbot $(checkcert --san-only --sep " -d " --pre www.example.com")``
|
||||
|
||||
|
||||
.. note:: Certbot was the specific reason this was added. As such, it will prepend the name queried to the list. The domain itself is not always included in the SAN list, checkcert will verify if the domain is in the list and will always prefix it if not. If example.com was not in the list of SANS, but ``checkcert --san --pre --sep " -d " example.com`` was used, '-d example.com' will be the first entry even if it was not part of the SANs.
|
||||
|
||||
Dumping the text version of a cert
|
||||
----------------------------------
|
||||
|
||||
``--dump`` will output the textual version of the certificate presented. Running ``checkcert --dump www.example.com`` will return::
|
||||
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number:
|
||||
0f:be:08:b0:85:4d:05:73:8a:b0:cc:e1:c9:af:ee:c9
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=US, O=DigiCert Inc, CN=DigiCert TLS RSA SHA256 2020 CA1
|
||||
Validity
|
||||
Not Before: Nov 24 00:00:00 2020 GMT
|
||||
Not After : Dec 25 23:59:59 2021 GMT
|
||||
Subject: C=US, ST=California, L=Los Angeles, O=Internet Corporation for Assigned Names and Numbers, CN=www.example.org
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
RSA Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:ba:fc:ee:cc:ca:0a:08:ff:0e:93:1d:b3:be:0b:
|
||||
9c:03:96:22:9e:b1:4f:10:ae:51:40:fd:53:5f:b3:
|
||||
c4:61:40:28:04:ee:a2:e6:12:00:b0:82:85:98:5c:
|
||||
6d:5b:6b:20:84:44:62:95:4e:6b:76:7c:50:70:5d:
|
||||
df:13:1d:ec:63:83:ad:63:a5:52:04:f2:cf:84:ba:
|
||||
db:2a:8c:c7:2e:b4:3c:64:df:eb:61:36:fe:86:03:
|
||||
54:79:3e:cd:03:59:8f:ef:c2:04:93:10:23:e2:a1:
|
||||
b9:b6:58:b8:26:ae:35:68:26:d4:94:2b:7b:7a:ab:
|
||||
86:5e:89:08:9a:10:be:51:8e:48:a5:01:19:4b:4b:
|
||||
4a:0f:8b:ee:da:4b:19:d3:84:1e:b6:9d:24:f2:35:
|
||||
9d:02:f3:00:db:b5:b7:13:08:07:1c:d7:95:19:66:
|
||||
c9:3c:2d:03:9f:b4:6a:3f:0d:77:af:b8:45:c9:2e:
|
||||
53:a6:57:b2:c2:37:58:d6:70:7b:69:de:a4:71:95:
|
||||
d9:6c:47:1a:15:9e:d9:b9:ea:c0:e9:19:0f:18:4f:
|
||||
8f:b2:76:51:6f:5a:05:26:46:28:5e:29:ac:ba:f9:
|
||||
15:16:15:9e:1d:05:c2:18:2d:5c:b8:35:92:ac:cd:
|
||||
dc:a5:0b:ce:cc:a1:f6:bd:2e:dd:d7:9f:b3:1a:5b:
|
||||
38:23
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:B7:6B:A2:EA:A8:AA:84:8C:79:EA:B4:DA:0F:98:B2:C5:95:76:B9:F4
|
||||
|
||||
X509v3 Subject Key Identifier:
|
||||
26:1A:F8:E4:B1:B0:72:84:CE:DA:81:06:D2:27:98:FB:ED:3A:3D:17
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net
|
||||
X509v3 Key Usage: critical
|
||||
Digital Signature, Key Encipherment
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Server Authentication, TLS Web Client Authentication
|
||||
X509v3 CRL Distribution Points:
|
||||
|
||||
Full Name:
|
||||
URI:http://crl3.digicert.com/DigiCertTLSRSASHA2562020CA1.crl
|
||||
|
||||
Full Name:
|
||||
URI:http://crl4.digicert.com/DigiCertTLSRSASHA2562020CA1.crl
|
||||
|
||||
X509v3 Certificate Policies:
|
||||
Policy: 2.16.840.1.114412.1.1
|
||||
CPS: https://www.digicert.com/CPS
|
||||
Policy: 2.23.140.1.2.2
|
||||
|
||||
Authority Information Access:
|
||||
OCSP - URI:http://ocsp.digicert.com
|
||||
CA Issuers - URI:http://cacerts.digicert.com/DigiCertTLSRSASHA2562020CA1.crt
|
||||
|
||||
X509v3 Basic Constraints: critical
|
||||
CA:FALSE
|
||||
CT Precertificate SCTs:
|
||||
Signed Certificate Timestamp:
|
||||
Version : v1 (0x0)
|
||||
Log ID : F6:5C:94:2F:D1:77:30:22:14:54:18:08:30:94:56:8E:
|
||||
E3:4D:13:19:33:BF:DF:0C:2F:20:0B:CC:4E:F1:64:E3
|
||||
Timestamp : Nov 24 19:32:04.334 2020 GMT
|
||||
Extensions: none
|
||||
Signature : ecdsa-with-SHA256
|
||||
30:46:02:21:00:A4:6B:A8:D0:43:A4:F1:07:32:2D:ED:
|
||||
9C:39:7D:77:E8:73:C1:9F:ED:22:4A:00:C5:BE:9A:C9:
|
||||
B5:B6:12:DC:B1:02:21:00:8D:E8:5F:8A:C7:52:CD:0D:
|
||||
A1:23:D5:B5:BB:DB:DB:62:13:88:22:D6:70:EC:83:5E:
|
||||
3F:C9:AC:94:4C:8C:58:3A
|
||||
Signed Certificate Timestamp:
|
||||
Version : v1 (0x0)
|
||||
Log ID : 5C:DC:43:92:FE:E6:AB:45:44:B1:5E:9A:D4:56:E6:10:
|
||||
37:FB:D5:FA:47:DC:A1:73:94:B2:5E:E6:F6:C7:0E:CA
|
||||
Timestamp : Nov 24 19:32:04.429 2020 GMT
|
||||
Extensions: none
|
||||
Signature : ecdsa-with-SHA256
|
||||
30:45:02:20:6A:AC:11:FA:05:09:12:FF:9B:8E:89:30:
|
||||
DF:0E:05:6E:CA:8E:59:CC:ED:B5:C2:0A:3C:33:34:A8:
|
||||
B0:33:DA:AC:02:21:00:DA:D8:5C:51:6D:64:0A:A6:AA:
|
||||
3D:8B:35:20:13:3A:6A:97:4F:76:B9:67:CB:BE:FC:CC:
|
||||
A4:57:67:B4:3F:1B:BD
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
a7:2a:10:30:5c:b8:6b:7a:1b:f8:66:38:f6:e9:a0:0a:d5:13:
|
||||
82:82:f8:65:89:57:a5:b8:eb:13:29:1d:84:6c:ec:fb:e3:05:
|
||||
11:d7:1e:31:5e:0e:e2:c0:00:e5:6d:06:48:be:3d:55:6f:ba:
|
||||
b7:11:35:b6:ea:c4:cf:84:f1:30:4c:bb:33:9e:11:17:2b:c9:
|
||||
d2:19:4b:2c:d0:ad:5f:17:23:84:e1:df:17:a2:3b:a8:7f:69:
|
||||
29:7c:48:a6:61:5f:26:3f:75:e2:3b:5b:a3:36:b3:1c:cd:e3:
|
||||
04:57:30:1f:fc:c9:fa:4b:8e:48:80:58:27:9c:a2:c7:c3:26:
|
||||
dc:17:02:fa:e6:6c:ea:81:01:5c:92:8f:d3:18:08:17:70:7a:
|
||||
c2:a3:4b:6c:3a:fa:e3:cf:f6:fe:7e:c9:56:e5:a5:4e:1b:14:
|
||||
4f:a9:98:9d:79:b1:1e:c3:ab:b1:0d:15:85:a9:46:b6:e5:c2:
|
||||
58:e8:5a:fe:c8:14:28:68:90:c6:b8:c8:94:7f:e1:0f:89:fa:
|
||||
a7:d6:09:37:a1:62:b7:00:27:b5:be:f1:b1:5e:45:28:06:b3:
|
||||
54:15:e6:c3:c8:ac:82:01:ce:86:e2:2b:e1:7a:e4:bd:4c:cb:
|
||||
9c:5e:d0:62:c2:61:bd:8b:5a:62:b6:76:30:bc:46:0f:e3:45:
|
||||
23:c0:64:5f
|
60
docs/source/shell_completion.rst
Normal file
60
docs/source/shell_completion.rst
Normal file
|
@ -0,0 +1,60 @@
|
|||
Shell Completion
|
||||
================
|
||||
|
||||
In order to use shell completion, checkcert must first be installed via ``pip install checkcert``. That generates the `checkcert` entry point that ultimately is used for the command completion. These instructions will not work if using a git cloned version.
|
||||
|
||||
Completion scripts are provided in the source's "completion" directory. You may copy those to source in your shell's rc files.
|
||||
|
||||
These steps are basically copied from Click's Documentation, since that is what is generating the completions. Check `Click's completion documentation <https://click.palletsprojects.com/en/8.0.x/shell-completion/>`_ for more details.
|
||||
|
||||
Obtaining from github
|
||||
---------------------
|
||||
|
||||
Download the needed completion script from github. Using curl, you could download the zsh completion with
|
||||
|
||||
``curl -LO https://raw.githubusercontent.com/kellya/checkcert/main/completions/checkcert-complete.zsh``
|
||||
|
||||
Then just append ``source /path/to/checkcert-complete.zsh`` to your .zshrc.
|
||||
|
||||
Bash will be pretty much the same, just use:
|
||||
|
||||
``curl -LO https://raw.githubusercontent.com/kellya/checkcert/main/completions/checkcert-complete.sh`` instead
|
||||
|
||||
|
||||
Generating Completion script
|
||||
----------------------------
|
||||
|
||||
If you do not have access to the files from github, but have checkcert installed, you may generate the completion scripts for inclusion in your shell rc file.
|
||||
|
||||
zsh
|
||||
^^^
|
||||
|
||||
``_CHECKCERT_COMPLETE=zsh_source checkcert > ~/.checkcert-complete.zsh``
|
||||
|
||||
Then in .zshrc, add a ``source ~/.checkcert-complete.zsh``
|
||||
|
||||
.. note:: There are various plugin directories that could be used to automatically install a completion. There are many options, so this doc just highlights a way that will work.
|
||||
|
||||
bash
|
||||
^^^^
|
||||
|
||||
The bash method is pretty much the same as zsh.
|
||||
|
||||
``_CHECKCERT_COMPLETE=bash_source checkcert > ~/.checkcert-complete.bash``
|
||||
|
||||
Generating Completion via eval
|
||||
------------------------------
|
||||
|
||||
Instead of generating a script to execute, you may use eval to generate the completions. This is a little quicker to implement; however there is a speed trade-off as the shell has to run this each time.
|
||||
|
||||
zsh
|
||||
^^^
|
||||
|
||||
Execute the following: ``eval "$(_CHECKCERT_COMPLETE=zsh_source checkcert)"``. You may put this in your ~/.zshrc to persist the setting.
|
||||
|
||||
bash
|
||||
^^^^
|
||||
|
||||
Execute the following: ``eval "$(_CHECKCERT_COMPLETE=bash_source checkcert)"``
|
||||
|
||||
|
400
poetry.lock
generated
400
poetry.lock
generated
|
@ -1,6 +1,14 @@
|
|||
[[package]]
|
||||
name = "alabaster"
|
||||
version = "0.7.12"
|
||||
description = "A configurable sidebar-enabled Sphinx theme"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "astroid"
|
||||
version = "2.8.0"
|
||||
version = "2.8.2"
|
||||
description = "An abstract syntax tree for Python with inference support."
|
||||
category = "dev"
|
||||
optional = false
|
||||
|
@ -33,6 +41,17 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
|
|||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
|
||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
|
||||
|
||||
[[package]]
|
||||
name = "babel"
|
||||
version = "2.9.1"
|
||||
description = "Internationalization utilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.dependencies]
|
||||
pytz = ">=2015.7"
|
||||
|
||||
[[package]]
|
||||
name = "bleach"
|
||||
version = "4.1.0"
|
||||
|
@ -119,7 +138,7 @@ toml = ["toml"]
|
|||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "3.4.8"
|
||||
version = "35.0.0"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
category = "main"
|
||||
optional = false
|
||||
|
@ -132,9 +151,9 @@ cffi = ">=1.12"
|
|||
docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"]
|
||||
docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"]
|
||||
pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
|
||||
sdist = ["setuptools-rust (>=0.11.4)"]
|
||||
sdist = ["setuptools_rust (>=0.11.4)"]
|
||||
ssh = ["bcrypt (>=3.1.5)"]
|
||||
test = ["pytest (>=6.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
|
||||
test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "docutils"
|
||||
|
@ -186,6 +205,14 @@ category = "dev"
|
|||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "imagesize"
|
||||
version = "1.2.0"
|
||||
description = "Getting image size from png/jpeg/jpeg2000/gif file"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "4.8.1"
|
||||
|
@ -244,6 +271,20 @@ python-versions = ">=3.6"
|
|||
test = ["pytest", "pytest-trio", "pytest-asyncio", "testpath", "trio", "async-timeout"]
|
||||
trio = ["trio", "async-generator"]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.0.2"
|
||||
description = "A very fast and expressive template engine."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=2.0"
|
||||
|
||||
[package.extras]
|
||||
i18n = ["Babel (>=2.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "keyring"
|
||||
version = "23.2.1"
|
||||
|
@ -270,6 +311,14 @@ category = "dev"
|
|||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.0.1"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "mccabe"
|
||||
version = "0.6.1"
|
||||
|
@ -278,6 +327,31 @@ category = "dev"
|
|||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "0.910"
|
||||
description = "Optional static typing for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.dependencies]
|
||||
mypy-extensions = ">=0.4.3,<0.5.0"
|
||||
toml = "*"
|
||||
typing-extensions = ">=3.7.4"
|
||||
|
||||
[package.extras]
|
||||
dmypy = ["psutil (>=4.0)"]
|
||||
python2 = ["typed-ast (>=1.4.0,<1.5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "0.4.3"
|
||||
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "21.0"
|
||||
|
@ -342,7 +416,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
|||
|
||||
[[package]]
|
||||
name = "pyflakes"
|
||||
version = "2.3.1"
|
||||
version = "2.4.0"
|
||||
description = "passive checker of Python programs"
|
||||
category = "dev"
|
||||
optional = false
|
||||
|
@ -460,6 +534,14 @@ docs = ["Sphinx (==1.3.6)"]
|
|||
mypy = ["mypy", "types-requests"]
|
||||
test = ["coverage (>=5,<6)", "pytest (>=5,<6)", "pytest-xdist (>=1,<2)", "pytest-mock (>=2,<3)", "responses (==0.13.3)", "mock (==1.3.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2021.3"
|
||||
description = "World timezone definitions, modern and historical"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pywin32-ctypes"
|
||||
version = "0.2.0"
|
||||
|
@ -470,7 +552,7 @@ python-versions = "*"
|
|||
|
||||
[[package]]
|
||||
name = "readme-renderer"
|
||||
version = "29.0"
|
||||
version = "30.0"
|
||||
description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse"
|
||||
category = "dev"
|
||||
optional = false
|
||||
|
@ -480,10 +562,9 @@ python-versions = "*"
|
|||
bleach = ">=2.1.0"
|
||||
docutils = ">=0.13.1"
|
||||
Pygments = ">=2.5.1"
|
||||
six = "*"
|
||||
|
||||
[package.extras]
|
||||
md = ["cmarkgfm (>=0.5.0,<0.6.0)"]
|
||||
md = ["cmarkgfm (>=0.5.0,<0.7.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
|
@ -576,6 +657,131 @@ category = "dev"
|
|||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "snowballstemmer"
|
||||
version = "2.1.0"
|
||||
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "sphinx"
|
||||
version = "4.2.0"
|
||||
description = "Python documentation generator"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
alabaster = ">=0.7,<0.8"
|
||||
babel = ">=1.3"
|
||||
colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
|
||||
docutils = ">=0.14,<0.18"
|
||||
imagesize = "*"
|
||||
Jinja2 = ">=2.3"
|
||||
packaging = "*"
|
||||
Pygments = ">=2.0"
|
||||
requests = ">=2.5.0"
|
||||
snowballstemmer = ">=1.1"
|
||||
sphinxcontrib-applehelp = "*"
|
||||
sphinxcontrib-devhelp = "*"
|
||||
sphinxcontrib-htmlhelp = ">=2.0.0"
|
||||
sphinxcontrib-jsmath = "*"
|
||||
sphinxcontrib-qthelp = "*"
|
||||
sphinxcontrib-serializinghtml = ">=1.1.5"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinxcontrib-websupport"]
|
||||
lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.900)", "docutils-stubs", "types-typed-ast", "types-pkg-resources", "types-requests"]
|
||||
test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"]
|
||||
|
||||
[[package]]
|
||||
name = "sphinx-rtd-theme"
|
||||
version = "1.0.0"
|
||||
description = "Read the Docs theme for Sphinx"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
|
||||
|
||||
[package.dependencies]
|
||||
docutils = "<0.18"
|
||||
sphinx = ">=1.6"
|
||||
|
||||
[package.extras]
|
||||
dev = ["transifex-client", "sphinxcontrib-httpdomain", "bump2version"]
|
||||
|
||||
[[package]]
|
||||
name = "sphinxcontrib-applehelp"
|
||||
version = "1.0.2"
|
||||
description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.extras]
|
||||
lint = ["flake8", "mypy", "docutils-stubs"]
|
||||
test = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "sphinxcontrib-devhelp"
|
||||
version = "1.0.2"
|
||||
description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.extras]
|
||||
lint = ["flake8", "mypy", "docutils-stubs"]
|
||||
test = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "sphinxcontrib-htmlhelp"
|
||||
version = "2.0.0"
|
||||
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
lint = ["flake8", "mypy", "docutils-stubs"]
|
||||
test = ["pytest", "html5lib"]
|
||||
|
||||
[[package]]
|
||||
name = "sphinxcontrib-jsmath"
|
||||
version = "1.0.1"
|
||||
description = "A sphinx extension which renders display math in HTML via JavaScript"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.extras]
|
||||
test = ["pytest", "flake8", "mypy"]
|
||||
|
||||
[[package]]
|
||||
name = "sphinxcontrib-qthelp"
|
||||
version = "1.0.3"
|
||||
description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.extras]
|
||||
lint = ["flake8", "mypy", "docutils-stubs"]
|
||||
test = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "sphinxcontrib-serializinghtml"
|
||||
version = "1.1.5"
|
||||
description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.extras]
|
||||
lint = ["flake8", "mypy", "docutils-stubs"]
|
||||
test = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
|
@ -674,7 +880,7 @@ python-versions = "*"
|
|||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.5.1"
|
||||
version = "3.6.0"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
category = "dev"
|
||||
optional = false
|
||||
|
@ -687,12 +893,16 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "f9e3c5502f1aa3c91b8c2a76f7f4c14d39cb84281143d987cf9e8ed043efa175"
|
||||
content-hash = "e6d761e045c3a735d1e875f5384d01840d28850284d11b5252823516c6d9c55f"
|
||||
|
||||
[metadata.files]
|
||||
alabaster = [
|
||||
{file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
|
||||
{file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
|
||||
]
|
||||
astroid = [
|
||||
{file = "astroid-2.8.0-py3-none-any.whl", hash = "sha256:dcc06f6165f415220013801642bd6c9808a02967070919c4b746c6864c205471"},
|
||||
{file = "astroid-2.8.0.tar.gz", hash = "sha256:fe81f80c0b35264acb5653302ffbd935d394f1775c5e4487df745bf9c2442708"},
|
||||
{file = "astroid-2.8.2-py3-none-any.whl", hash = "sha256:9eaeaf92b3e21b70bec1a262e7eb118d2e96294892a5de595c92a12adc80dfc2"},
|
||||
{file = "astroid-2.8.2.tar.gz", hash = "sha256:304e99c129794f2cfda584a12b71fde85205da950e2f330f4be09150525ae949"},
|
||||
]
|
||||
atomicwrites = [
|
||||
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
|
||||
|
@ -702,6 +912,10 @@ attrs = [
|
|||
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
|
||||
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
|
||||
]
|
||||
babel = [
|
||||
{file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"},
|
||||
{file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"},
|
||||
]
|
||||
bleach = [
|
||||
{file = "bleach-4.1.0-py2.py3-none-any.whl", hash = "sha256:4d2651ab93271d1129ac9cbc679f524565cc8a1b791909c4a51eac4446a15994"},
|
||||
{file = "bleach-4.1.0.tar.gz", hash = "sha256:0900d8b37eba61a802ee40ac0061f8c2b5dee29c1927dd1d233e075ebf5a71da"},
|
||||
|
@ -828,23 +1042,26 @@ coverage = [
|
|||
{file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"},
|
||||
]
|
||||
cryptography = [
|
||||
{file = "cryptography-3.4.8-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:a00cf305f07b26c351d8d4e1af84ad7501eca8a342dedf24a7acb0e7b7406e14"},
|
||||
{file = "cryptography-3.4.8-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:f44d141b8c4ea5eb4dbc9b3ad992d45580c1d22bf5e24363f2fbf50c2d7ae8a7"},
|
||||
{file = "cryptography-3.4.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0a7dcbcd3f1913f664aca35d47c1331fce738d44ec34b7be8b9d332151b0b01e"},
|
||||
{file = "cryptography-3.4.8-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34dae04a0dce5730d8eb7894eab617d8a70d0c97da76b905de9efb7128ad7085"},
|
||||
{file = "cryptography-3.4.8-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1eb7bb0df6f6f583dd8e054689def236255161ebbcf62b226454ab9ec663746b"},
|
||||
{file = "cryptography-3.4.8-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:9965c46c674ba8cc572bc09a03f4c649292ee73e1b683adb1ce81e82e9a6a0fb"},
|
||||
{file = "cryptography-3.4.8-cp36-abi3-win32.whl", hash = "sha256:21ca464b3a4b8d8e86ba0ee5045e103a1fcfac3b39319727bc0fc58c09c6aff7"},
|
||||
{file = "cryptography-3.4.8-cp36-abi3-win_amd64.whl", hash = "sha256:3520667fda779eb788ea00080124875be18f2d8f0848ec00733c0ec3bb8219fc"},
|
||||
{file = "cryptography-3.4.8-pp36-pypy36_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d2a6e5ef66503da51d2110edf6c403dc6b494cc0082f85db12f54e9c5d4c3ec5"},
|
||||
{file = "cryptography-3.4.8-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a305600e7a6b7b855cd798e00278161b681ad6e9b7eca94c721d5f588ab212af"},
|
||||
{file = "cryptography-3.4.8-pp36-pypy36_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:3fa3a7ccf96e826affdf1a0a9432be74dc73423125c8f96a909e3835a5ef194a"},
|
||||
{file = "cryptography-3.4.8-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:d9ec0e67a14f9d1d48dd87a2531009a9b251c02ea42851c060b25c782516ff06"},
|
||||
{file = "cryptography-3.4.8-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5b0fbfae7ff7febdb74b574055c7466da334a5371f253732d7e2e7525d570498"},
|
||||
{file = "cryptography-3.4.8-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94fff993ee9bc1b2440d3b7243d488c6a3d9724cc2b09cdb297f6a886d040ef7"},
|
||||
{file = "cryptography-3.4.8-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:8695456444f277af73a4877db9fc979849cd3ee74c198d04fc0776ebc3db52b9"},
|
||||
{file = "cryptography-3.4.8-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:cd65b60cfe004790c795cc35f272e41a3df4631e2fb6b35aa7ac6ef2859d554e"},
|
||||
{file = "cryptography-3.4.8.tar.gz", hash = "sha256:94cc5ed4ceaefcbe5bf38c8fba6a21fc1d365bb8fb826ea1688e3370b2e24a1c"},
|
||||
{file = "cryptography-35.0.0-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:d57e0cdc1b44b6cdf8af1d01807db06886f10177469312fbde8f44ccbb284bc9"},
|
||||
{file = "cryptography-35.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:ced40344e811d6abba00295ced98c01aecf0c2de39481792d87af4fa58b7b4d6"},
|
||||
{file = "cryptography-35.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:54b2605e5475944e2213258e0ab8696f4f357a31371e538ef21e8d61c843c28d"},
|
||||
{file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7b7ceeff114c31f285528ba8b390d3e9cfa2da17b56f11d366769a807f17cbaa"},
|
||||
{file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d69645f535f4b2c722cfb07a8eab916265545b3475fdb34e0be2f4ee8b0b15e"},
|
||||
{file = "cryptography-35.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2d0e0acc20ede0f06ef7aa58546eee96d2592c00f450c9acb89c5879b61992"},
|
||||
{file = "cryptography-35.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:07bb7fbfb5de0980590ddfc7f13081520def06dc9ed214000ad4372fb4e3c7f6"},
|
||||
{file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7eba2cebca600a7806b893cb1d541a6e910afa87e97acf2021a22b32da1df52d"},
|
||||
{file = "cryptography-35.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:18d90f4711bf63e2fb21e8c8e51ed8189438e6b35a6d996201ebd98a26abbbe6"},
|
||||
{file = "cryptography-35.0.0-cp36-abi3-win32.whl", hash = "sha256:c10c797ac89c746e488d2ee92bd4abd593615694ee17b2500578b63cad6b93a8"},
|
||||
{file = "cryptography-35.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:7075b304cd567694dc692ffc9747f3e9cb393cc4aa4fb7b9f3abd6f5c4e43588"},
|
||||
{file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a688ebcd08250eab5bb5bca318cc05a8c66de5e4171a65ca51db6bd753ff8953"},
|
||||
{file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99915d6ab265c22873f1b4d6ea5ef462ef797b4140be4c9d8b179915e0985c6"},
|
||||
{file = "cryptography-35.0.0-pp36-pypy36_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:928185a6d1ccdb816e883f56ebe92e975a262d31cc536429041921f8cb5a62fd"},
|
||||
{file = "cryptography-35.0.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76"},
|
||||
{file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22a38e96118a4ce3b97509443feace1d1011d0571fae81fc3ad35f25ba3ea999"},
|
||||
{file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad"},
|
||||
{file = "cryptography-35.0.0-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:abb5a361d2585bb95012a19ed9b2c8f412c5d723a9836418fab7aaa0243e67d2"},
|
||||
{file = "cryptography-35.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1ed82abf16df40a60942a8c211251ae72858b25b7421ce2497c2eb7a1cee817c"},
|
||||
{file = "cryptography-35.0.0.tar.gz", hash = "sha256:9933f28f70d0517686bd7de36166dda42094eac49415459d9bdf5e7df3e0086d"},
|
||||
]
|
||||
docutils = [
|
||||
{file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"},
|
||||
|
@ -865,6 +1082,10 @@ idna = [
|
|||
{file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
|
||||
{file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
|
||||
]
|
||||
imagesize = [
|
||||
{file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"},
|
||||
{file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"},
|
||||
]
|
||||
importlib-metadata = [
|
||||
{file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
|
||||
{file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
|
||||
|
@ -886,6 +1107,10 @@ jeepney = [
|
|||
{file = "jeepney-0.7.1-py3-none-any.whl", hash = "sha256:1b5a0ea5c0e7b166b2f5895b91a08c14de8915afda4407fb5022a195224958ac"},
|
||||
{file = "jeepney-0.7.1.tar.gz", hash = "sha256:fa9e232dfa0c498bd0b8a3a73b8d8a31978304dcef0515adc859d4e096f96f4f"},
|
||||
]
|
||||
jinja2 = [
|
||||
{file = "Jinja2-3.0.2-py3-none-any.whl", hash = "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c"},
|
||||
{file = "Jinja2-3.0.2.tar.gz", hash = "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45"},
|
||||
]
|
||||
keyring = [
|
||||
{file = "keyring-23.2.1-py3-none-any.whl", hash = "sha256:bd2145a237ed70c8ce72978b497619ddfcae640b6dcf494402d5143e37755c6e"},
|
||||
{file = "keyring-23.2.1.tar.gz", hash = "sha256:6334aee6073db2fb1f30892697b1730105b5e9a77ce7e61fca6b435225493efe"},
|
||||
|
@ -914,10 +1139,75 @@ lazy-object-proxy = [
|
|||
{file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"},
|
||||
{file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"},
|
||||
]
|
||||
markupsafe = [
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
|
||||
{file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
|
||||
{file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
|
||||
{file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
|
||||
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
|
||||
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
|
||||
]
|
||||
mccabe = [
|
||||
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
|
||||
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
|
||||
]
|
||||
mypy = [
|
||||
{file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"},
|
||||
{file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"},
|
||||
{file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"},
|
||||
{file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"},
|
||||
{file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"},
|
||||
{file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"},
|
||||
{file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"},
|
||||
{file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"},
|
||||
{file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"},
|
||||
{file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"},
|
||||
{file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"},
|
||||
{file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"},
|
||||
{file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"},
|
||||
{file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"},
|
||||
{file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"},
|
||||
{file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"},
|
||||
{file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"},
|
||||
{file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"},
|
||||
{file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"},
|
||||
{file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"},
|
||||
{file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"},
|
||||
{file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"},
|
||||
{file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"},
|
||||
]
|
||||
mypy-extensions = [
|
||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"},
|
||||
{file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"},
|
||||
|
@ -943,8 +1233,8 @@ pycparser = [
|
|||
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
|
||||
]
|
||||
pyflakes = [
|
||||
{file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
|
||||
{file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
|
||||
{file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
|
||||
{file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
|
||||
]
|
||||
pygments = [
|
||||
{file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"},
|
||||
|
@ -974,13 +1264,17 @@ python-semantic-release = [
|
|||
{file = "python-semantic-release-7.19.2.tar.gz", hash = "sha256:8ca0e5f72d31e7b0603b95caad6fb2d5315483ac1fadd86648771966d9ec6f2c"},
|
||||
{file = "python_semantic_release-7.19.2-py3-none-any.whl", hash = "sha256:b2c8bb16a643fee0831be4d06138bc1440ebd4f252c3397d41abde179ea56852"},
|
||||
]
|
||||
pytz = [
|
||||
{file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
|
||||
{file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
|
||||
]
|
||||
pywin32-ctypes = [
|
||||
{file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"},
|
||||
{file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"},
|
||||
]
|
||||
readme-renderer = [
|
||||
{file = "readme_renderer-29.0-py2.py3-none-any.whl", hash = "sha256:63b4075c6698fcfa78e584930f07f39e05d46f3ec97f65006e430b595ca6348c"},
|
||||
{file = "readme_renderer-29.0.tar.gz", hash = "sha256:92fd5ac2bf8677f310f3303aa4bce5b9d5f9f2094ab98c29f13791d7b805a3db"},
|
||||
{file = "readme_renderer-30.0-py2.py3-none-any.whl", hash = "sha256:3286806450d9961d6e3b5f8a59f77e61503799aca5155c8d8d40359b4e1e1adc"},
|
||||
{file = "readme_renderer-30.0.tar.gz", hash = "sha256:8299700d7a910c304072a7601eafada6712a5b011a20139417e1b1e9f04645d8"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"},
|
||||
|
@ -1014,6 +1308,42 @@ smmap = [
|
|||
{file = "smmap-4.0.0-py2.py3-none-any.whl", hash = "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"},
|
||||
{file = "smmap-4.0.0.tar.gz", hash = "sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182"},
|
||||
]
|
||||
snowballstemmer = [
|
||||
{file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"},
|
||||
{file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"},
|
||||
]
|
||||
sphinx = [
|
||||
{file = "Sphinx-4.2.0-py3-none-any.whl", hash = "sha256:98a535c62a4fcfcc362528592f69b26f7caec587d32cd55688db580be0287ae0"},
|
||||
{file = "Sphinx-4.2.0.tar.gz", hash = "sha256:94078db9184491e15bce0a56d9186e0aec95f16ac20b12d00e06d4e36f1058a6"},
|
||||
]
|
||||
sphinx-rtd-theme = [
|
||||
{file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"},
|
||||
{file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"},
|
||||
]
|
||||
sphinxcontrib-applehelp = [
|
||||
{file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"},
|
||||
{file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"},
|
||||
]
|
||||
sphinxcontrib-devhelp = [
|
||||
{file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"},
|
||||
{file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"},
|
||||
]
|
||||
sphinxcontrib-htmlhelp = [
|
||||
{file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"},
|
||||
{file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"},
|
||||
]
|
||||
sphinxcontrib-jsmath = [
|
||||
{file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
|
||||
{file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
|
||||
]
|
||||
sphinxcontrib-qthelp = [
|
||||
{file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"},
|
||||
{file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"},
|
||||
]
|
||||
sphinxcontrib-serializinghtml = [
|
||||
{file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"},
|
||||
{file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"},
|
||||
]
|
||||
toml = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
|
@ -1051,6 +1381,6 @@ wrapt = [
|
|||
{file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"},
|
||||
]
|
||||
zipp = [
|
||||
{file = "zipp-3.5.1-py3-none-any.whl", hash = "sha256:8dc6c4d5a809d659067cc713f76bcf42fae8ae641db12fddfa93694a15abc96b"},
|
||||
{file = "zipp-3.5.1.tar.gz", hash = "sha256:1fc9641b26f3bd81069b7738b039f2819cab6e3fc3399a953e19d92cc81eff4d"},
|
||||
{file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"},
|
||||
{file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"},
|
||||
]
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
[tool.poetry]
|
||||
name = "checkcert"
|
||||
version = "0.2.0"
|
||||
version = "0.7.2"
|
||||
description = "CLI to check tls cert information and determine validity"
|
||||
authors = ["Alex Kelly <kellya@arachnitech.com>"]
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/kellya/checkcert"
|
||||
documentation = "https://checkcert.readthedocs.io"
|
||||
license = "MIT"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
|
@ -18,6 +19,9 @@ coverage = "^5.5"
|
|||
pylint = "^2.11.1"
|
||||
pyflakes = "^2.3.1"
|
||||
pytest = "^6.2.5"
|
||||
Sphinx = "^4.2.0"
|
||||
sphinx-rtd-theme = "^1.0.0"
|
||||
mypy = "^0.910"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
|
|
|
@ -1,29 +1,85 @@
|
|||
""" Tests to validate checkcert"""
|
||||
from click.testing import CliRunner
|
||||
from checkcert.checkcert import main as cert_main
|
||||
from checkcert.checkcert import __version__ as cert_version
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
|
||||
|
||||
def test_main():
|
||||
if cert_main:
|
||||
response = runner.invoke(cert_main, ["www.franklin.edu"])
|
||||
assert response.exit_code == 0
|
||||
response = runner.invoke(cert_main, ["www.franklin.edu:443"])
|
||||
assert response.exit_code == 0
|
||||
"""validate the core function returns correctly for:
|
||||
a domain
|
||||
a domain and a port
|
||||
no names specified
|
||||
a list of domains
|
||||
a list of domains with a port specified on one
|
||||
"""
|
||||
response = runner.invoke(cert_main, ["www.franklin.edu"])
|
||||
assert response.exit_code == 0
|
||||
response = runner.invoke(cert_main, ["www.franklin.edu:443"])
|
||||
assert response.exit_code == 0
|
||||
response = runner.invoke(cert_main, ["www.franklin.edu", "--no-color"])
|
||||
assert response.exit_code == 0
|
||||
response = runner.invoke(cert_main, ["www.franklin.edu", "library.franklin.edu"])
|
||||
assert response.exit_code == 0
|
||||
response = runner.invoke(cert_main, ["www.franklin.edu", "--valid"])
|
||||
assert response.exit_code == 0
|
||||
response = runner.invoke(
|
||||
cert_main, ["www.franklin.edu:443", "library.franklin.edu"]
|
||||
)
|
||||
assert response.exit_code == 0
|
||||
|
||||
|
||||
def test_version():
|
||||
"""get the version output and ensure it matches the version var"""
|
||||
response = runner.invoke(cert_main, ["--version"])
|
||||
assert response.exit_code == 0
|
||||
assert cert_version in response.output
|
||||
|
||||
|
||||
def test_dump():
|
||||
"""verify that --dump outputs data"""
|
||||
response = runner.invoke(cert_main, ["www.franklin.edu", "--dump"])
|
||||
assert response.exit_code == 0
|
||||
assert "www.franklin.edu" in response.output
|
||||
|
||||
|
||||
def test_san():
|
||||
"""verify --san outputs correctly"""
|
||||
response = runner.invoke(cert_main, ["www.franklin.edu", "--san"])
|
||||
assert response.exit_code == 0
|
||||
|
||||
|
||||
def test_san_only():
|
||||
"""verify --san outputs correctly"""
|
||||
check_domain = "example.com"
|
||||
response = runner.invoke(cert_main, ["--san-only", check_domain])
|
||||
assert response.exit_code == 0
|
||||
sep_string = ","
|
||||
response = runner.invoke(
|
||||
cert_main, ["--san-only", "--sep", sep_string, check_domain]
|
||||
)
|
||||
assert response.exit_code == 0
|
||||
assert sep_string in response.output
|
||||
response = runner.invoke(
|
||||
cert_main, ["--san-only", "--sep", sep_string, "--pre", check_domain]
|
||||
)
|
||||
assert response.exit_code == 0
|
||||
assert response.output.startswith(sep_string)
|
||||
response = runner.invoke(
|
||||
cert_main, ["--san-only", "--sep", sep_string, "--pre", "cs.franklin.edu"]
|
||||
)
|
||||
assert response.exit_code == 0
|
||||
assert response.output.startswith(sep_string)
|
||||
|
||||
|
||||
def test_bad_cert():
|
||||
"""verify an expired certificate works"""
|
||||
response = runner.invoke(cert_main, ["support.bluequill.com", "--san"])
|
||||
assert response.exit_code == 0
|
||||
|
||||
|
||||
def test_from_file():
|
||||
"""Verify loading domains from file"""
|
||||
response = runner.invoke(cert_main, ["--filename", "ci/test_domains.txt"])
|
||||
assert response.exit_code == 0
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue