Skip to content

Commit

Permalink
Mitigate code scanning alerts
Browse files Browse the repository at this point in the history
Add bandit to the code scanning workflow
  • Loading branch information
ObserverOfTime committed Dec 4, 2023
1 parent 49cfb59 commit e6a1a41
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 22 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: "Mark previous prerelease"
continue-on-error: true
run: |-
export tag="$(gh release view --json tagName -t '{{.tagName}}')"
tag="$(gh release view --json tagName -t '{{.tagName}}')"
# skip if the new tag has a different minor version
[[ ${GITHUB_REF_NAME%.*} == ${tag%.*} ]] || exit 0
gh release edit "$tag" --prerelease
Expand Down Expand Up @@ -49,8 +49,8 @@ jobs:
username: "GitHub",
avatar_url: "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
embeds: [{author: {$name, $icon_url, url: $author_url}, $url, $title, $description}]
}' | curl -d@- -Ssf "$RELEASE_WEBHOOK" -H 'Content-Type: application/json' \
-A "GitHub-Actions ($GITHUB_REPOSITORY, v0.1.1)"
}' | curl "$RELEASE_WEBHOOK" -H 'Content-Type: application/json' \
-Ssf -d@- -A "GitHub-Actions ($GITHUB_REPOSITORY, v0.1.1)"
env:
RELEASE_WEBHOOK: ${{secrets.RELEASE_WEBHOOK}}

Expand Down
23 changes: 18 additions & 5 deletions .github/workflows/scans.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,30 @@ jobs:
- name: "Initialize CodeQL"
uses: github/codeql-action/init@v2
with:
languages: javascript, python
- name: "Perform CodeQL Analysis"
languages: javascript,python
- name: "Perform CodeQL analysis"
uses: github/codeql-action/analyze@v2

bandit:
name: "Bandit"
runs-on: ubuntu-latest
steps:
- name: "Checkout repository"
uses: actions/checkout@v4
- name: "Install bandit"
run: pip install bandit[toml] bandit-sarif-formatter
- name: "Run bandit"
run: bandit -c pyproject.toml -f sarif -o bandit.sarif -r .
- name: "Upload bandit analysis"
uses: github/codeql-action/upload-sarif@v2
with:
category: bandit
sarif_file: bandit.sarif

dependency-review:
name: "Dependencies"
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- name: "Checkout repository"
uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,4 @@ static/styles/_variables.scss

# GitHub Stuff
docs/changes.md
*.sarif
15 changes: 7 additions & 8 deletions config/management/commands/fs2import.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@

from io import StringIO
from os.path import abspath, join
from typing import TYPE_CHECKING
from xml.etree import ElementTree as ET
from typing import TYPE_CHECKING, List

from django.core.files import File
from django.core.management import BaseCommand, CommandError, call_command
from django.db.utils import IntegrityError

from defusedxml import ElementTree as ET

from groups.models import Group
from reader.models import Chapter, Page, Series

if TYPE_CHECKING: # pragma: no cover
from argparse import ArgumentParser
from typing import List
Elem = ET.Element
Elems = List[ET.Element]
from xml.etree.ElementTree import Element # nosec: B405


class Command(BaseCommand):
Expand Down Expand Up @@ -183,18 +182,18 @@ def handle(self, *args: str, **options: str):
self._print_success('Successfully imported FoolSlide2 data.')

@staticmethod
def _get_element(tables: Elems, name: str) -> Elems:
def _get_element(tables: List[Element], name: str) -> List[Element]:
return list(filter(
lambda t: t.attrib['name'].endswith(name), tables
))

@staticmethod
def _get_column(table: Elem, name: str) -> str:
def _get_column(table: Element, name: str) -> str:
elem = table.find(f'column[@name="{name}"]')
return getattr(elem, 'text', None) or ''

@staticmethod
def _sort_children(tables: Elems, name: str) -> Elems:
def _sort_children(tables: List[Element], name: str) -> List[Element]:
return sorted(tables, key=lambda p: Command._get_column(p, name))

def _print(self, text: str, **kwargs):
Expand Down
9 changes: 7 additions & 2 deletions config/templatetags/custom_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ def jsonld(value: Dict, element_id: str) -> str:
.. seealso:: :tag:`json_script template tag <json-script>`
"""
sep = (',', ':')
# Escape special HTML characters for JSON output
escapes = {ord('>'): '\\u003E', ord('<'): '\\u003C', ord('&'): '\\u0026'}
jstr = dumps(value, cls=DjangoJSONEncoder, indent=None, separators=sep)
return format_html(
'<script id="{}" type="application/ld+json">{}</script>',
element_id, mark_safe(jstr.translate(escapes))
element_id, mark_safe(jstr.translate(escapes)) # nosec: B308
)


Expand Down Expand Up @@ -72,7 +73,11 @@ def get_type(link: str) -> str:
if (key := 'type.' + basename(link.lower())) in cache:
return cache.get(key) # pragma: no cover
try:
with urlopen(Request(link, method='HEAD')) as response:
# Disallow non-HTTP(S) schemes
if not link.startswith(('http://', 'https://')):
raise Exception('Invalid scheme')
request = Request(link, method='HEAD')
with urlopen(request) as response: # nosec: B310
type_ = response.info().get_content_type()
cache.add(key, type_)
return type_
Expand Down
4 changes: 2 additions & 2 deletions config/tests/test_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ def test_jsonld():
element_id = 'whatever'
tag = jsonld(value, element_id)
assert tag.startswith(f'<script id="{element_id}"')
pattern = r'<script.*?>(.*?)</script>'
body = match(pattern, tag).group(1) # lgtm[py/bad-tag-filter]
pattern = r'<script.*?>(.*?)</script>' # lgtm[py/bad-tag-filter]
body = match(pattern, tag).group(1)
assert '<' not in body
assert '>' not in body
assert '&' not in body
Expand Down
20 changes: 20 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ dev = [
# https://github.com/PyCQA/flake8/issues/234
"flake8-pyproject>=1.2",
"isort>=5.12",
"bandit~=1.7",
"mypy~=1.6",
"pytest~=7.4",
"pytest-cov>=4.1",
Expand Down Expand Up @@ -146,6 +147,25 @@ ignore_missing_imports = true
disable_error_code = ["misc", "override"]
plugins = ["mypy_django_plugin.main", "mypy_drf_plugin.main"]

[tool.bandit]
exclude_dirs = [
"./.git/*",
"./.venv/*",
"./.eggs/*",
"./.mypy_cache/*",
"./__pycache__/*",
"./docs/*",
"./build/*",
"./dist/*",
"./*/tests/*"
]
skips = [
"B104", # 0.0.0.0 is only used in debug mode
"B301", # pickled data is verified with HMAC
"B403", # pickled data is verified with HMAC
"B703", # covered by B308 (mark_safe)
]

[tool.django-stubs]
django_settings_module = "MangAdventure.settings"

Expand Down
2 changes: 1 addition & 1 deletion reader/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def get_form(self, request: HttpRequest, obj: Optional[Series]
'<b>{number}</b>: The number of the chapter.',
'<b>{date}</b>: The chapter\'s upload date (YYYY-MM-DD).',
'<b>{series}</b>: The title of the series.'
)))
))) # nosec: B308
if 'manager' in form.base_fields:
form.base_fields['manager'].initial = request.user.id
if request.user.is_superuser: # pragma: no cover
Expand Down
2 changes: 1 addition & 1 deletion reader/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from shutil import rmtree
from threading import Lock, Thread
from typing import Any, List, Tuple, Union
from xml.etree import ElementTree as ET
from xml.etree import ElementTree as ET # nosec: B405
from zipfile import ZipFile

from django.conf import settings
Expand Down

0 comments on commit e6a1a41

Please sign in to comment.