Coverage for lintro / parsers / gitleaks / gitleaks_issue.py: 100%

29 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Gitleaks issue model for secret detection findings.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass, field 

6from typing import ClassVar 

7 

8from lintro.enums.severity_level import SeverityLevel 

9from lintro.parsers.base_issue import BaseIssue 

10 

11 

12@dataclass 

13class GitleaksIssue(BaseIssue): 

14 """Represents a secret detection finding from Gitleaks. 

15 

16 Attributes: 

17 DISPLAY_FIELD_MAP: Mapping of display field names to attribute names. 

18 DEFAULT_SEVERITY: Defaults to ERROR (security tool). 

19 rule_id: The rule ID that triggered the detection (e.g., aws-access-key-id). 

20 description: Description of the secret type detected. 

21 secret: The detected secret value (should be redacted for display). 

22 entropy: Shannon entropy of the detected secret. 

23 tags: List of tags associated with the rule. 

24 fingerprint: Unique identifier for this finding. 

25 end_line: End line number of the finding. 

26 end_column: End column number of the finding. 

27 match: The matched pattern string. 

28 commit: Git commit hash if scanning git history. 

29 author: Git author if scanning git history. 

30 email: Git author email if scanning git history. 

31 date: Git commit date if scanning git history. 

32 commit_message: Git commit message if scanning git history. 

33 """ 

34 

35 DISPLAY_FIELD_MAP: ClassVar[dict[str, str]] = { 

36 **BaseIssue.DISPLAY_FIELD_MAP, 

37 "code": "rule_id", 

38 # message uses the computed value from __post_init__ (default mapping) 

39 } 

40 

41 DEFAULT_SEVERITY: ClassVar[SeverityLevel] = SeverityLevel.ERROR 

42 

43 rule_id: str = field(default="") 

44 description: str = field(default="") 

45 secret: str = field(default="") 

46 entropy: float = field(default=0.0) 

47 tags: list[str] = field(default_factory=list) 

48 fingerprint: str = field(default="") 

49 end_line: int = field(default=0) 

50 end_column: int = field(default=0) 

51 match: str = field(default="") 

52 commit: str = field(default="") 

53 author: str = field(default="") 

54 email: str = field(default="") 

55 date: str = field(default="") 

56 commit_message: str = field(default="") 

57 

58 def __post_init__(self) -> None: 

59 """Initialize the inherited message field.""" 

60 self.message = self._get_message() 

61 

62 def _get_message(self) -> str: 

63 """Get the formatted issue message. 

64 

65 Returns: 

66 Formatted issue message with redacted secret. 

67 """ 

68 redacted_hint = "[REDACTED]" if self.secret else "" 

69 parts = [ 

70 f"[{self.rule_id}]" if self.rule_id else "", 

71 self.description, 

72 redacted_hint, 

73 ] 

74 return " ".join(part for part in parts if part)