Skip to content

Commit

Permalink
feat: Introduce new headers (OS, Python) version
Browse files Browse the repository at this point in the history
  • Loading branch information
Alaa Ben Fatma committed Dec 14, 2022
1 parent 72d57ff commit 4987744
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 3 deletions.
52 changes: 50 additions & 2 deletions ggshield/scan/scan_context.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,56 @@
import logging
import platform
import sys
import uuid
from dataclasses import dataclass
from typing import Dict, Optional, Union
from functools import lru_cache
from pathlib import Path
from typing import Dict, Optional, Tuple, Union

from ggshield import __version__

from .scan_mode import ScanMode


logger = logging.getLogger(__name__)


@lru_cache(None)
def get_os_info() -> Tuple[str, str]:
if sys.platform.lower() == "linux":
return parse_os_release(Path("/etc/os-release"))
else:
return platform.system().lower(), platform.version()


def parse_os_release(os_release_path: Path) -> Tuple[str, str]:
"""
Extract and return Linux's OS-name and OS-Version
If extraction fails, we return ('linux', 'unknown')
"""
error_tuple = "linux", "unknown"

try:
with open(os_release_path) as f:
lines = f.readlines()

# Build a dictionary from the os-release file contents
data_dict = {}
for line in lines:
if "=" in line:
key, value = line.split("=")
key, value = key.strip(), value.strip()
data_dict[key] = value.strip('"')

if "ID" not in data_dict:
return error_tuple

return data_dict["ID"], data_dict.get("VERSION_ID", "unknown")
except Exception as exc:
logger.warning(f"Failed to read Linux OS name and version: {exc}")
return error_tuple


@dataclass
class ScanContext:
scan_mode: Union[ScanMode, str]
Expand All @@ -15,6 +59,8 @@ class ScanContext:

def __post_init__(self) -> None:
self.command_id = str(uuid.uuid4())
self.os_name, self.os_version = get_os_info()
self.python_version = platform.python_version()

def get_http_headers(self) -> Dict[str, str]:
"""
Expand All @@ -26,8 +72,10 @@ def get_http_headers(self) -> Dict[str, str]:
"Version": __version__,
"Command-Path": self.command_path,
"Command-Id": self.command_id,
"OS-Name": self.os_name,
"OS-Version": self.os_version,
"Python-Version": self.python_version,
}

if self.extra_headers:
headers = {**headers, **self.extra_headers}

Expand Down
4 changes: 4 additions & 0 deletions tests/unit/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,10 @@ def isolated_fs(fs):
# add cassettes dir
cassettes_dir = join(dirname(realpath(__file__)), "cassettes")
fs.add_real_directory(cassettes_dir)
# Add a fake OS-release file. It describes a linux OS
mock_contents = """ID="ubuntu"\nVERSION_ID="22.04"\n"""
f = fs.create_file("/etc/os-release")
f.set_contents(mock_contents)


def write_text(filename: str, content: str):
Expand Down
29 changes: 28 additions & 1 deletion tests/unit/core/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
from typing import Dict, List, Optional
import sys
from typing import AnyStr, Dict, List, Optional, Tuple
from unittest.mock import Mock, patch

import click
Expand All @@ -21,6 +22,7 @@
)
from ggshield.scan import Commit, File, Files, ScanContext, ScanMode, SecretScanner
from ggshield.scan.repo import cd
from ggshield.scan.scan_context import parse_os_release
from tests.unit.conftest import (
_PATCH_WITH_NONEWLINE_BEFORE_SECRET,
_SECRET_RAW_FILE,
Expand Down Expand Up @@ -324,3 +326,28 @@ def test_load_dot_env_loads_git_root_env(
with cd(str(sub1_sub2_dir)):
load_dot_env()
load_dotenv_mock.assert_called_once_with(str(git_root_dotenv), override=True)


@pytest.mark.skipif(
sys.platform.lower() != "linux", reason="This test is only relevant on Linux."
)
@pytest.mark.parametrize(
"file_contents, file_permissions, expected_tuple",
[
('ID="ubuntu"\nVERSION_ID=""22.04""', 777, ("ubuntu", "22.04")),
('ID="arch"', 777, ("arch", "unknown")),
("", 777, ("linux", "unknown")),
('ID="ubuntu"\nVERSION_ID="22.04"\n', 640, ("linux", "unknown")),
],
)
def test_parse_os_release(
tmp_path,
file_contents: AnyStr,
file_permissions: int,
expected_tuple: Tuple[str, str],
):
file = tmp_path / "os-release"

file.write_text(file_contents)
file.chmod(file_permissions)
assert parse_os_release(file) == expected_tuple
6 changes: 6 additions & 0 deletions tests/unit/scan/test_scan.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import platform
from unittest.mock import ANY, Mock, patch

from click import Command, Context, Group
Expand All @@ -7,6 +8,7 @@
from ggshield.core.cache import Cache
from ggshield.scan import Commit, ScanContext, ScanMode, SecretScanner
from ggshield.scan.repo import cd
from ggshield.scan.scan_context import get_os_info
from tests.unit.conftest import UNCHECKED_SECRET_PATCH


Expand All @@ -24,6 +26,7 @@ def test_request_headers(scan_mock: Mock, client):
c._patch = UNCHECKED_SECRET_PATCH

with Context(Command("bar"), info_name="bar") as ctx:
os_name, os_version = get_os_info()
ctx.parent = Context(Group("foo"), info_name="foo")
scanner = SecretScanner(
client=client,
Expand All @@ -40,6 +43,9 @@ def test_request_headers(scan_mock: Mock, client):
"GGShield-Version": __version__,
"GGShield-Command-Path": "foo bar",
"GGShield-Command-Id": ANY,
"GGShield-OS-Name": os_name,
"GGShield-OS-Version": os_version,
"GGShield-Python-Version": platform.python_version(),
"mode": "path",
},
ignore_known_secrets=None,
Expand Down

0 comments on commit 4987744

Please sign in to comment.