diff --git a/README.rst b/README.rst index 7abe7bb..483ab7d 100644 --- a/README.rst +++ b/README.rst @@ -1,25 +1,100 @@ Inlinehashes ============ -TODO +A small tool and library to generate the hashes of inline content that needs to be whitelisted when serving an HTML document +with a `Content-Security-Policy `_ (as the name indicates, +using ``unsafe-inline`` is not recommended.) + +You provide the HTML content (directly or through a file path or URL) then ``inlinehashes`` will parse the document and provide +you with a list of elements that need to be explicitly added to the CSP header/tag. + +The tool can be specially useful for scenarios where you use/include external software solutions in your website or application +(such as a 3rd party CMS), since it will allow you to detect changes after updates and edit you CSP accordingly. + +*Quick note: Always verify the content you are whitelisting and be careful when fetching live website data, since any XSS +code will be included in the results.* + +**At the moment this package is still in a very early stage, so it still doesn't detect all possible items and the current API +might change with future releases.** + +Inline content that is currently detected: + +* ```` tags +* ```` tags + Installation ------------ -TODO +Using pip you just need to ``pip install inlinehashes`` Usage ----- +The package can be used through 2 different ways, either by using the CLI interface or programmatically in your python project. +Bellow you can find a quick summary of the available functionality. + CLI app ....... -TODO +This is the available functionality: + +.. code:: + + usage: inlinehashes [-h] [-a {sha256,sha384,sha512}] [-f] [-o OUTPUT] source + + positional arguments: + source URL or local HTML file to check + + optional arguments: + -h, --help show this help message and exit + -a {sha256,sha384,sha512}, --alg {sha256,sha384,sha512} + Hash algorithm to use (default: sha256) + -f, --full Include full content in the output + -o OUTPUT, --output OUTPUT + Store output in a file. + +Here is an example of the output: + +.. code:: + + $inlinehashes https://ovalerio.net -a sha384 + [ + { + "content": "\n html {\n height: 100%;\n }\n ", + "hash": "sha384-Ku20lQH5qbr4EDPzXD2rf25rEHJNswNYRUNMPjYl7jCe0eHJYDe0gFdQpnKkFUTv" + } + ] + Library ....... -TODO +Here is the same example, but using the python shell: + +.. code:: python + + >>> import requests + >>> import inlinehashes + >>> content = requests.get("https://ovalerio.net").text + >>> inlines = inlinehashes.parse(content) + >>> inlines + [Inline(content=' + html { + height: 100%; + } + ')] + >>> first = inlines[0] + >>> first.short_content + '\n html {\n height: 100%;\n }\n ' + >>> first.sha256 + 'sha256-aDiwGOuSD1arNOxmHSp89QLe81yheSUQFjqpWHYCpRY=' + >>> first.sha384 + 'sha384-Ku20lQH5qbr4EDPzXD2rf25rEHJNswNYRUNMPjYl7jCe0eHJYDe0gFdQpnKkFUTv' + >>> first.sha512 + 'sha512-cBO6RNy87Tx3HmpXRZUs/DPxGq9ZOqIZ9cCyDum0kNZeLEWVvW5DtYFRmHcQawnAoWeeRmll4aJeLXTb2OLBlA==' + >>> first.content + '\n html {\n height: 100%;\n }\n body {\n background-image: url("data:image/png;base64,iVBORw0KGgoAAAANS..." Contributions ------------- diff --git a/inlinehashes/app.py b/inlinehashes/app.py index ecf0376..2caf378 100644 --- a/inlinehashes/app.py +++ b/inlinehashes/app.py @@ -35,10 +35,17 @@ def run_cli() -> None: parser = argparse.ArgumentParser() parser.add_argument("source", help="URL or local HTML file to check") parser.add_argument( - "-a", "--alg", help="Hash algorithm to use (default: sha256)", default="sha256" + "-a", + "--alg", + help="Hash algorithm to use (default: sha256)", + default="sha256", + choices=["sha256", "sha384", "sha512"], ) parser.add_argument( - "-f", "--full", help="Include full content in the output", action="store_true" + "-f", + "--full", + help="Include full content in the output", + action="store_true", ) parser.add_argument("-o", "--output", help="Store output in a file.") args = parser.parse_args() diff --git a/inlinehashes/lib.py b/inlinehashes/lib.py index 00484c8..a978b5b 100644 --- a/inlinehashes/lib.py +++ b/inlinehashes/lib.py @@ -55,6 +55,12 @@ class Inline: h_b64 = base64.b64encode(h.digest()).decode("utf8") return f"sha512-{h_b64}" + def __repr__(self) -> str: + return f"Inline(content='{self.content}')" + + def __str__(self) -> str: + return f"Inline(content='{self.short_content}...')" + def parse(content: str, target: str = "all") -> List[Inline]: """Parses an HTML document and extracts.""" diff --git a/pyproject.toml b/pyproject.toml index 9ec9202..e5a1d1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ black = "^22.1.0" mypy = "^0.940" [tool.poetry.scripts] -cli = "inlinehashes.app:run_cli" +inlinehashes = "inlinehashes.app:run_cli" [build-system] requires = ["poetry-core>=1.0.0"]