Skip to content

Commit

Permalink
Merge pull request #17 from juerkkil/major_refactor
Browse files Browse the repository at this point in the history
restructure project for packaging
  • Loading branch information
juerkkil authored Oct 19, 2024
2 parents 073ff6e + c92265d commit 357f87e
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 37 deletions.
29 changes: 29 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
venv/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2016 Jussi-Pekka Erkkila
Copyright (c) 2016-2024 Jussi-Pekka Erkkila

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
62 changes: 43 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,63 @@
# securityheaders
# secheaders
Python script to check HTTP security headers


Same functionality as securityheaders.io but as Python script. Also checks some server/version headers. Written and tested using Python 3.4.

With minor modifications could be used as a library for other projects.

## Installation

The following assumes you have Python installed and command `python` refers to python version >= 3.4.

### Run without installation

1. Clone into repository
2. Run `python -m secheaders`

### Installation

1. Clone into repository
2. `python -m build`
3. `pip install dist/secheaders-0.1.0-py3-none-any.whl`
4. Run `secheaders --help`



### Usage
```
$ python securityheaders.py --help
usage: securityheaders.py [-h] [--max-redirects N] URL
$ secheaders --help
usage: secheaders [-h] [--max-redirects N] [--no-check-certificate] URL
Check HTTP security headers
positional arguments:
URL Target URL
URL Target URL
optional arguments:
-h, --help show this help message and exit
--max-redirects N Max redirects, set 0 to disable (default: 2)
$
options:
-h, --help show this help message and exit
--max-redirects N Max redirects, set 0 to disable (default: 2)
--no-check-certificate
Do not verify TLS certificate chain (default: False)
```

### Output

### Example output
```
$ python securityheaders.py --max-redirects 5 https://secfault.fi
Header 'x-xss-protection' is missing ... [ WARN ]
Header 'x-content-type-options' is missing ... [ WARN ]
$ secheaders example.com
Header 'x-frame-options' is missing ... [ WARN ]
Header 'strict-transport-security' is missing ... [ WARN ]
Header 'content-security-policy' is missing ... [ WARN ]
Header 'x-powered-by' is missing ... [ OK ]
Header 'x-frame-options' contains value 'DENY' ... [ OK ]
Header 'strict-transport-security' contains value 'max-age=63072000' ... [ OK ]
Header 'access-control-allow-origin' is missing ... [ OK ]
Header 'server' contains value 'nginx/1.10.1' ... [ WARN ]
Header 'x-content-type-options' is missing ... [ WARN ]
Header 'x-xss-protection' is missing ... [ OK ]
Header 'referrer-policy' is missing ... [ WARN ]
Header 'permissions-policy' is missing ... [ WARN ]
Header 'server' contains value 'ECAcc (nyd/D187) ... [ WARN ]
HTTPS supported ... [ OK ]
HTTPS valid certificate ... [ OK ]
HTTP -> HTTPS redirect ... [ OK ]
$
HTTP -> HTTPS redirect ... [ WARN ]
```

## Note

The project renamed (2024-10-19) from **securityheaders** to **secheaders** to avoid confusion with PyPI package with similar name.
31 changes: 31 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "secheaders"
version = "0.1.0"
requires-python = ">=3.4"
authors = [
{name = "Jussi-Pekka Erkkilä", email = "[email protected]"},
]
maintainers = [
{name = "Jussi-Pekka Erkkilä", email = "[email protected]"}
]
description = "Scan HTTP security headers"
readme = "README.md"
license = {file = "LICENSE"}
keywords = ["web", "security"]
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: Developers",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Topic :: Security",
]

[project.scripts]
secheaders = "secheaders.securityheaders:main"

Empty file added secheaders/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions secheaders/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
if __name__ == "__main__":
from .securityheaders import main
main()
File renamed without changes.
10 changes: 10 additions & 0 deletions secheaders/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class SecurityHeadersException(Exception):
pass


class InvalidTargetURL(SecurityHeadersException):
pass


class UnableToConnect(SecurityHeadersException):
pass
23 changes: 8 additions & 15 deletions securityheaders.py → secheaders/securityheaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,9 @@
import sys
from urllib.parse import ParseResult, urlparse

import utils
from constants import DEFAULT_TIMEOUT, DEFAULT_URL_SCHEME, EVAL_WARN


class SecurityHeadersException(Exception):
pass


class InvalidTargetURL(SecurityHeadersException):
pass


class UnableToConnect(SecurityHeadersException):
pass
from . import utils
from .constants import DEFAULT_TIMEOUT, DEFAULT_URL_SCHEME, EVAL_WARN
from .exceptions import SecurityHeadersException, InvalidTargetURL, UnableToConnect


class SecurityHeaders():
Expand Down Expand Up @@ -219,7 +208,7 @@ def check_headers(self):
return retval


if __name__ == "__main__":
def main():
parser = argparse.ArgumentParser(description='Check HTTP security headers',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('url', metavar='URL', type=str, help='Target URL')
Expand Down Expand Up @@ -269,3 +258,7 @@ def check_headers(self):
utils.print_ok("HTTP -> HTTPS redirect")
else:
utils.print_warning("HTTP -> HTTPS redirect")


if __name__ == "__main__":
main()
5 changes: 3 additions & 2 deletions utils.py → secheaders/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Tuple


from constants import EVAL_WARN, EVAL_OK
from .constants import EVAL_WARN, EVAL_OK


def eval_x_frame_options(contents: str) -> Tuple[int, list]:
Expand Down Expand Up @@ -97,7 +97,8 @@ def eval_permissions_policy(contents: str) -> Tuple[int, list]:
feat_policy = pp_parsed.get(feature)
if feat_policy is None:
pp_unsafe = True
notes.append("Privacy-sensitive feature '{}' not defined in permission-policy, always allowed.".format(feature))
notes.append("Privacy-sensitive feature '{}' not defined in permission-policy, always allowed.".format(
feature))
elif '*' in feat_policy:
pp_unsafe = True
notes.append("Privacy-sensitive feature '{}' allowed from unsafe origin '*'".format(feature))
Expand Down
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import setuptools

setuptools.setup()

0 comments on commit 357f87e

Please sign in to comment.