Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/codeql/codeql-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Read by `Initialize CodeQL` in codeql-analysis.yml, through its `config-file`
# input.
name: "PyPCAPKit CodeQL config"

# #2130: the py/clear-text-logging-sensitive-data and
# py/clear-text-storage-sensitive-data alerts that came back each time a pull
# request moved a line in pcapkit/ began in tests/. A test that builds a pcapng
# Decryption Secrets Block calls its bytes `secrets`, and CodeQL takes a value
# whose name says secret for secret data. Python is not built, so an ignored
# path is left out of the database and cannot be a flow source. Both queries,
# and every other, still run over pcapkit/. The alert at the logger call in
# pcapkit/utilities/warnings.py begins there instead, at the Decryption Secrets
# Block's own `secrets_length` field, so this does not clear it.
paths-ignore:
- tests
3 changes: 3 additions & 0 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ jobs:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

# #2130: the config leaves tests/ out of the scan.
- name: Initialize CodeQL
uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
with:
config-file: ./.github/codeql/codeql-config.yml

- name: Autobuild
uses: github/codeql-action/autobuild@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
Expand Down
28 changes: 28 additions & 0 deletions docs/source/contributing/workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,34 @@ result of ``analyze`` stands. Where the alerts cannot be read, the step writes a
warning, the report marks them unavailable, and ``analyze``'s result stands
unchanged.

.. _CodeQL Configuration:

CodeQL Configuration (`#2130 <https://github.com/JarryShaw/PyPCAPKit/issues/2130>`__)
-------------------------------------------------------------------------------------

``Initialize CodeQL`` in ``codeql-analysis.yml`` reads
:file:`.github/codeql/codeql-config.yml` through its ``config-file`` input. The
config's ``paths-ignore`` leaves out :file:`tests` and changes nothing else, so the
default queries still run over :mod:`pcapkit` and every other path. Python is not
built, so an ignored file is left out of the analysis (`Specifying directories to
scan
<https://docs.github.com/en/code-security/reference/code-scanning/workflow-configuration-options#specifying-directories-to-scan>`__)
and cannot be the source of a data flow.

``py/clear-text-logging-sensitive-data`` and
``py/clear-text-storage-sensitive-data`` call a value secret from its name alone,
by CodeQL's `sensitive-data heuristics
<https://github.com/github/codeql/blob/main/shared/concepts/codeql/concepts/internal/SensitiveDataHeuristics.qll>`__.
The tests that build a pcapng Decryption Secrets Block call its bytes
``secrets``. Through :func:`~pcapkit.interface.core.extract` those bytes reached
``logger`` calls in :mod:`pcapkit.foundation.extraction` and the buffer file's
write in :mod:`pcapkit.corekit.io`, and each alert's dismissal was lost whenever
a pull request moved the line it named. The alert on the ``logger`` call in
:mod:`pcapkit.utilities.warnings` begins in :mod:`pcapkit` itself, at the block's
``secrets_length`` field, the spec's name for it, so it is still raised and still
dismissed by hand. :file:`tests/project/test_codeql_config_2130.py` pins the
config and the input.

.. _Action Pins:

Action Pins (`#1968 <https://github.com/JarryShaw/PyPCAPKit/issues/1968>`__)
Expand Down
76 changes: 76 additions & 0 deletions tests/project/test_codeql_config_2130.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
"""#2130: CodeQL scans everything but :file:`tests`.

``py/clear-text-logging-sensitive-data`` and
``py/clear-text-storage-sensitive-data`` call a value secret from its name
alone. The tests that build a pcapng Decryption Secrets Block call its bytes
``secrets``, and those bytes reached ``logger`` calls in :mod:`pcapkit`, as a
capture's stream, its name, or its magic number, and a buffer file's write.
Each dismissal was lost when a
pull request moved the line the alert named. Python is not built, so a path the
config ignores is not extracted and cannot be a flow source.

* :class:`TestTheInitStep` checks ``Initialize CodeQL`` in
:file:`codeql-analysis.yml` reads :file:`.github/codeql/codeql-config.yml`
and nothing else.
* :class:`TestTheConfig` checks the config ignores :file:`tests` and changes
nothing else: every query still runs, over every other path.

"""

from __future__ import annotations

import unittest
from typing import TYPE_CHECKING

from tests._tiers import ROOT
from tests._workflows import load, loads

if TYPE_CHECKING:
from typing import Any

#: Test tier (#1958): reads a workflow and the CodeQL config, a question about the tree.
__tier__ = 'LATEST'

NAME = 'codeql-analysis.yml'
CONFIG = '.github/codeql/codeql-config.yml'


def config() -> 'dict[str, Any]':
return loads((ROOT / CONFIG).read_text(encoding='utf-8'))


class TestTheInitStep(unittest.TestCase):
"""``Initialize CodeQL`` names the config file."""

def test_it_reads_the_config(self) -> None:
[step] = [entry for entry in load(NAME)['jobs']['analyze']['steps']
if entry.get('name') == 'Initialize CodeQL']
self.assertEqual(step.get('with'), {'config-file': f'./{CONFIG}'})
self.assertTrue((ROOT / CONFIG).is_file())

def test_no_other_step_passes_a_config(self) -> None:
"""An inline ``config`` would take precedence over the file."""
for entry in load(NAME)['jobs']['analyze']['steps']:
with self.subTest(step=entry.get('name')):
self.assertNotIn('config', entry.get('with') or {})
if entry.get('name') != 'Initialize CodeQL':
self.assertNotIn('config-file', entry.get('with') or {})


class TestTheConfig(unittest.TestCase):
""":file:`tests` left out, and the scan otherwise as it was."""

def test_only_the_tests_are_ignored(self) -> None:
self.assertEqual(config()['paths-ignore'], ['tests'])

def test_every_query_still_runs(self) -> None:
spec = config()
for key in ('query-filters', 'paths', 'disable-default-queries', 'queries', 'packs'):
with self.subTest(key=key):
self.assertNotIn(key, spec)
self.assertEqual(sorted(spec), ['name', 'paths-ignore'])


if __name__ == '__main__':
unittest.main()
Loading