mirror of https://github.com/dethos/inlinehashes
Add other types of output for the results
This commit is contained in:
parent
9bf236b60d
commit
20fc87f5cd
11
README.rst
11
README.rst
|
@ -43,18 +43,19 @@ This is the available functionality:
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
usage: inlinehashes [-h] [-a {sha256,sha384,sha512}] [-f] [-t {all,scripts,styles}] source
|
usage: inlinehashes [-h] [-a {sha256,sha384,sha512}] [-o {table,json,plain}] [-t {all,script-src,style-src}] source
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
source URL or local HTML file to check
|
source URL or local HTML file to check
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
-a {sha256,sha384,sha512}, --alg {sha256,sha384,sha512}
|
-a {sha256,sha384,sha512}, --alg {sha256,sha384,sha512}
|
||||||
Hash algorithm to use (default: sha256)
|
Hash algorithm to use (default: sha256)
|
||||||
-f, --full Include full content in the output
|
-o {table,json,plain}, --output {table,json,plain}
|
||||||
-t {all,scripts,styles}, --target {all,scripts,styles}
|
Format used to write the output (default: table)
|
||||||
Target inline content to look for
|
-t {all,script-src,style-src}, --target {all,script-src,style-src}
|
||||||
|
Target inline content to look for (default: all)
|
||||||
|
|
||||||
Here is an example of the output:
|
Here is an example of the output:
|
||||||
|
|
||||||
|
|
|
@ -10,17 +10,19 @@ from typing import List
|
||||||
from urllib.error import URLError
|
from urllib.error import URLError
|
||||||
from urllib.request import Request, urlopen
|
from urllib.request import Request, urlopen
|
||||||
|
|
||||||
|
from rich import box
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.syntax import Syntax
|
||||||
|
from rich.table import Table
|
||||||
|
|
||||||
import inlinehashes
|
import inlinehashes
|
||||||
|
|
||||||
|
|
||||||
def build_output(
|
def build_json_output(inlines: List[inlinehashes.lib.Inline], alg: str) -> Syntax:
|
||||||
inlines: List[inlinehashes.lib.Inline], alg: str, full: bool = False
|
|
||||||
) -> str:
|
|
||||||
"""Build a JSON output from a list of Inline objects."""
|
"""Build a JSON output from a list of Inline objects."""
|
||||||
snippet = "content" if full else "short_content"
|
|
||||||
out = [
|
out = [
|
||||||
{
|
{
|
||||||
"content": getattr(i, snippet),
|
"content": i.short_content,
|
||||||
"hash": getattr(i, alg),
|
"hash": getattr(i, alg),
|
||||||
"directive": i.directive,
|
"directive": i.directive,
|
||||||
"line": i.line,
|
"line": i.line,
|
||||||
|
@ -28,7 +30,32 @@ def build_output(
|
||||||
}
|
}
|
||||||
for i in inlines
|
for i in inlines
|
||||||
]
|
]
|
||||||
return json.dumps(out, indent=2)
|
return Syntax(json.dumps(out, indent=2), "JSON", theme="ansi_dark")
|
||||||
|
|
||||||
|
|
||||||
|
def build_plain_output(inlines: List[inlinehashes.lib.Inline], alg: str) -> str:
|
||||||
|
"""Build a simple output of an inline per line."""
|
||||||
|
return "\n".join(
|
||||||
|
[
|
||||||
|
f"[magenta]{i.directive}[/magenta] [cyan]{i.line}[/cyan] "
|
||||||
|
f"[green]{i.position}[/green] [default]{getattr(i, alg)}[/default]"
|
||||||
|
for i in inlines
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_table_output(inlines: List[inlinehashes.lib.Inline], alg: str) -> Table:
|
||||||
|
"""Build a table to output the inlines in a nicer way."""
|
||||||
|
table = Table(box=box.HORIZONTALS)
|
||||||
|
table.add_column("Directive", style="magenta")
|
||||||
|
table.add_column("Line", justify="right", style="cyan")
|
||||||
|
table.add_column("Position", justify="right", style="green")
|
||||||
|
table.add_column("Hash")
|
||||||
|
|
||||||
|
for i in inlines:
|
||||||
|
table.add_row(i.directive, str(i.line), str(i.position), getattr(i, alg))
|
||||||
|
|
||||||
|
return table
|
||||||
|
|
||||||
|
|
||||||
def run_cli() -> None:
|
def run_cli() -> None:
|
||||||
|
@ -43,21 +70,24 @@ def run_cli() -> None:
|
||||||
choices=["sha256", "sha384", "sha512"],
|
choices=["sha256", "sha384", "sha512"],
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-f",
|
"-o",
|
||||||
"--full",
|
"--output",
|
||||||
help="Include full content in the output",
|
help="Format used to write the output (default: table)",
|
||||||
action="store_true",
|
default="table",
|
||||||
|
choices=["table", "json", "plain"],
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-t",
|
"-t",
|
||||||
"--target",
|
"--target",
|
||||||
help="Target inline content to look for",
|
help="Target inline content to look for (default: all)",
|
||||||
default="all",
|
default="all",
|
||||||
choices=["all", "script-src", "style-src"],
|
choices=["all", "script-src", "style-src"],
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
path = args.source
|
path = args.source
|
||||||
target = args.target
|
target = args.target
|
||||||
|
output_format = args.output
|
||||||
|
console = Console()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if path.startswith("http://") or path.startswith("https://"):
|
if path.startswith("http://") or path.startswith("https://"):
|
||||||
|
@ -71,13 +101,19 @@ def run_cli() -> None:
|
||||||
with open(path, "r") as f:
|
with open(path, "r") as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
except (URLError, OSError) as error:
|
except (URLError, OSError) as error:
|
||||||
print(error)
|
console.print(error)
|
||||||
print(f"Failed to get source: {path}")
|
console.print(f"Failed to get source: {path}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
inlines = inlinehashes.parse(content, target)
|
inlines = inlinehashes.parse(content, target)
|
||||||
out = build_output(inlines, args.alg, bool(args.full))
|
if output_format == "json":
|
||||||
print(out)
|
out = build_json_output(inlines, args.alg)
|
||||||
|
elif output_format == "plain":
|
||||||
|
out = build_plain_output(inlines, args.alg)
|
||||||
|
else:
|
||||||
|
out = build_table_output(inlines, args.alg)
|
||||||
|
|
||||||
|
console.print(out)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -111,6 +111,43 @@ files = [
|
||||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markdown-it-py"
|
||||||
|
version = "2.1.0"
|
||||||
|
description = "Python port of markdown-it. Markdown parsing, done right!"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "markdown-it-py-2.1.0.tar.gz", hash = "sha256:cf7e59fed14b5ae17c0006eff14a2d9a00ed5f3a846148153899a0224e2c07da"},
|
||||||
|
{file = "markdown_it_py-2.1.0-py3-none-any.whl", hash = "sha256:93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
mdurl = ">=0.1,<1.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
benchmarking = ["psutil", "pytest", "pytest-benchmark (>=3.2,<4.0)"]
|
||||||
|
code-style = ["pre-commit (==2.6)"]
|
||||||
|
compare = ["commonmark (>=0.9.1,<0.10.0)", "markdown (>=3.3.6,<3.4.0)", "mistletoe (>=0.8.1,<0.9.0)", "mistune (>=2.0.2,<2.1.0)", "panflute (>=2.1.3,<2.2.0)"]
|
||||||
|
linkify = ["linkify-it-py (>=1.0,<2.0)"]
|
||||||
|
plugins = ["mdit-py-plugins"]
|
||||||
|
profiling = ["gprof2dot"]
|
||||||
|
rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
|
||||||
|
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mdurl"
|
||||||
|
version = "0.1.2"
|
||||||
|
description = "Markdown URL utilities"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
|
||||||
|
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy"
|
name = "mypy"
|
||||||
version = "0.991"
|
version = "0.991"
|
||||||
|
@ -229,6 +266,21 @@ files = [
|
||||||
dev = ["pre-commit", "tox"]
|
dev = ["pre-commit", "tox"]
|
||||||
testing = ["pytest", "pytest-benchmark"]
|
testing = ["pytest", "pytest-benchmark"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pygments"
|
||||||
|
version = "2.14.0"
|
||||||
|
description = "Pygments is a syntax highlighting package written in Python."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"},
|
||||||
|
{file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
plugins = ["importlib-metadata"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest"
|
name = "pytest"
|
||||||
version = "7.2.1"
|
version = "7.2.1"
|
||||||
|
@ -251,6 +303,25 @@ pluggy = ">=0.12,<2.0"
|
||||||
[package.extras]
|
[package.extras]
|
||||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rich"
|
||||||
|
version = "13.2.0"
|
||||||
|
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7.0"
|
||||||
|
files = [
|
||||||
|
{file = "rich-13.2.0-py3-none-any.whl", hash = "sha256:7c963f0d03819221e9ac561e1bc866e3f95a02248c1234daa48954e6d381c003"},
|
||||||
|
{file = "rich-13.2.0.tar.gz", hash = "sha256:f1a00cdd3eebf999a15d85ec498bfe0b1a77efe9b34f645768a54132ef444ac5"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
markdown-it-py = ">=2.1.0,<3.0.0"
|
||||||
|
pygments = ">=2.6.0,<3.0.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.0.229"
|
version = "0.0.229"
|
||||||
|
@ -304,4 +375,4 @@ files = [
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.11"
|
python-versions = "^3.11"
|
||||||
content-hash = "81f9d3306e76f9f7c39291bd7972216d852f39aba794b43afe2b9a0d4a7a2829"
|
content-hash = "ded8c6b34befe59dd43fd80d0a9027a54c17a97bc734445fb5978235f17cc0ca"
|
||||||
|
|
|
@ -21,6 +21,7 @@ classifiers = [
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.11"
|
python = "^3.11"
|
||||||
beautifulsoup4 = "^4.10.0"
|
beautifulsoup4 = "^4.10.0"
|
||||||
|
rich = "^13.2.0"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pytest = "^7.2.1"
|
pytest = "^7.2.1"
|
||||||
|
|
Loading…
Reference in New Issue