param-bharat commited on
Commit
553ced5
1 Parent(s): 581b398

docs: add documentation and minor refactors

Browse files
guardrails_genie/guardrails/secrets_detection/secrets_detection.py CHANGED
@@ -11,7 +11,13 @@ from guardrails_genie.guardrails.base import Guardrail
11
  from guardrails_genie.regex_model import RegexModel
12
 
13
 
14
- def load_secrets_patterns():
 
 
 
 
 
 
15
  default_patterns = {}
16
  patterns = (
17
  pathlib.Path(__file__).parent.absolute() / "secrets_patterns.jsonl"
@@ -23,10 +29,15 @@ def load_secrets_patterns():
23
  return default_patterns
24
 
25
 
 
26
  DEFAULT_SECRETS_PATTERNS = load_secrets_patterns()
27
 
28
 
29
  class REDACTION(str, Enum):
 
 
 
 
30
  REDACT_PARTIAL = "REDACT_PARTIAL"
31
  REDACT_ALL = "REDACT_ALL"
32
  REDACT_HASH = "REDACT_HASH"
@@ -34,6 +45,17 @@ class REDACTION(str, Enum):
34
 
35
 
36
  def redact(text: str, matches: list[str], redaction_type: REDACTION) -> str:
 
 
 
 
 
 
 
 
 
 
 
37
  for match in matches:
38
  if redaction_type == REDACTION.REDACT_PARTIAL:
39
  replacement = "[REDACTED:]" + match[:2] + ".." + match[-2:] + "[:REDACTED]"
@@ -50,20 +72,53 @@ def redact(text: str, matches: list[str], redaction_type: REDACTION) -> str:
50
 
51
 
52
  class SecretsDetectionSimpleResponse(BaseModel):
 
 
 
 
 
 
 
 
 
53
  contains_secrets: bool
54
  explanation: str
55
  redacted_text: Optional[str] = None
56
 
57
  @property
58
  def safe(self) -> bool:
59
- return not self.contains_entities
 
 
 
 
 
 
60
 
61
 
62
  class SecretsDetectionResponse(SecretsDetectionSimpleResponse):
 
 
 
 
 
 
 
63
  detected_secrets: dict[str, list[str]]
64
 
65
 
66
  class SecretsDetectionGuardrail(Guardrail):
 
 
 
 
 
 
 
 
 
 
 
67
  regex_model: RegexModel
68
  patterns: Union[dict[str, str], dict[str, list[str]]] = {}
69
  redaction: REDACTION
@@ -74,16 +129,22 @@ class SecretsDetectionGuardrail(Guardrail):
74
  redaction: REDACTION = REDACTION.REDACT_ALL,
75
  **kwargs,
76
  ):
 
 
 
 
 
 
 
 
77
  patterns = {}
78
  if use_defaults:
79
  patterns = DEFAULT_SECRETS_PATTERNS.copy()
80
  if kwargs.get("patterns"):
81
  patterns.update(kwargs["patterns"])
82
 
83
- # Create the RegexModel instance
84
  regex_model = RegexModel(patterns=patterns)
85
 
86
- # Initialize the base class with both the regex_model and patterns
87
  super().__init__(
88
  regex_model=regex_model,
89
  patterns=patterns,
@@ -94,22 +155,21 @@ class SecretsDetectionGuardrail(Guardrail):
94
  def guard(
95
  self,
96
  prompt: str,
97
- return_detected_types: bool = True,
98
  **kwargs,
99
  ) -> SecretsDetectionResponse | SecretsDetectionResponse:
100
  """
101
- Check if the input prompt contains any entities based on the regex patterns.
102
 
103
  Args:
104
- prompt: Input text to check for entities
105
- return_detected_types: If True, returns detailed entity type information
106
 
107
  Returns:
108
- SecretsDetectionResponse or SecretsDetectionResponse containing detection results
109
  """
110
  result = self.regex_model.check(prompt)
111
 
112
- # Create detailed explanation
113
  explanation_parts = []
114
  if result.matched_patterns:
115
  explanation_parts.append("Found the following secrets in the text:")
@@ -123,7 +183,7 @@ class SecretsDetectionGuardrail(Guardrail):
123
  for secret_type, matches in result.matched_patterns.items():
124
  redacted_text = redact(redacted_text, matches, self.redaction)
125
 
126
- if return_detected_types:
127
  return SecretsDetectionResponse(
128
  contains_secrets=not result.passed,
129
  detected_secrets=result.matched_patterns,
@@ -132,52 +192,7 @@ class SecretsDetectionGuardrail(Guardrail):
132
  )
133
  else:
134
  return SecretsDetectionSimpleResponse(
135
- contains_entities=not result.passed,
136
  explanation="\n".join(explanation_parts),
137
  redacted_text=redacted_text,
138
  )
139
-
140
-
141
- def main():
142
- weave.init(project_name="parambharat/guardrails-genie")
143
-
144
- guardrail = SecretsDetectionGuardrail(redaction=REDACTION.REDACT_ALL)
145
- dataset = [
146
- {
147
- "input": 'I need to pass a key\naws_secret_access_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"',
148
- },
149
- {
150
- "input": "My github token is: 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",
151
- },
152
- {
153
- "input": "My JWT token is: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
154
- },
155
- ]
156
-
157
- for item in dataset:
158
- # Check text for entities
159
- result = guardrail.guard(prompt=item["input"])
160
-
161
- # Access results
162
- print(f"Contains entities: {result.contains_secrets}")
163
- print(f"Detected entities: {result.detected_secrets}")
164
- print(f"Explanation: {result.explanation}")
165
- print(f"Anonymized text: {result.redacted_text}")
166
- # import regex as re
167
- #
168
- # sample_input = "My JWT token is: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
169
- # jwt_pattern = DEFAULT_SECRETS_PATTERNS["JwtToken"][0]
170
- # print(jwt_pattern)
171
- # pattern = re.compile(jwt_pattern)
172
- # print(pattern)
173
- # print(pattern.findall(sample_input))
174
-
175
- # import pandas as pd
176
- #
177
- # df = pd.read_json("secrets_patterns_bak.jsonl", lines=True)
178
- # df.loc[:, "patterns"] = df["patterns"].map(lambda x: [i[2:-1] for i in x])
179
- # df.to_json("secrets_patterns.jsonl", orient="records", lines=True)
180
-
181
-
182
- if __name__ == "__main__":
183
- main()
 
11
  from guardrails_genie.regex_model import RegexModel
12
 
13
 
14
+ def load_secrets_patterns() -> dict[str, list[str]]:
15
+ """
16
+ Load secret patterns from a JSONL file and return them as a dictionary.
17
+
18
+ Returns:
19
+ dict: A dictionary where keys are pattern names and values are lists of regex patterns.
20
+ """
21
  default_patterns = {}
22
  patterns = (
23
  pathlib.Path(__file__).parent.absolute() / "secrets_patterns.jsonl"
 
29
  return default_patterns
30
 
31
 
32
+ # Load default secret patterns from the JSONL file
33
  DEFAULT_SECRETS_PATTERNS = load_secrets_patterns()
34
 
35
 
36
  class REDACTION(str, Enum):
37
+ """
38
+ Enum for different types of redaction methods.
39
+ """
40
+
41
  REDACT_PARTIAL = "REDACT_PARTIAL"
42
  REDACT_ALL = "REDACT_ALL"
43
  REDACT_HASH = "REDACT_HASH"
 
45
 
46
 
47
  def redact(text: str, matches: list[str], redaction_type: REDACTION) -> str:
48
+ """
49
+ Redact the given matches in the text based on the redaction type.
50
+
51
+ Args:
52
+ text (str): The input text to redact.
53
+ matches (list[str]): List of strings to be redacted.
54
+ redaction_type (REDACTION): The type of redaction to apply.
55
+
56
+ Returns:
57
+ str: The redacted text.
58
+ """
59
  for match in matches:
60
  if redaction_type == REDACTION.REDACT_PARTIAL:
61
  replacement = "[REDACTED:]" + match[:2] + ".." + match[-2:] + "[:REDACTED]"
 
72
 
73
 
74
  class SecretsDetectionSimpleResponse(BaseModel):
75
+ """
76
+ A simple response model for secrets detection.
77
+
78
+ Attributes:
79
+ contains_secrets (bool): Indicates if secrets were detected.
80
+ explanation (str): Explanation of the detection result.
81
+ redacted_text (Optional[str]): The redacted text if secrets were found.
82
+ """
83
+
84
  contains_secrets: bool
85
  explanation: str
86
  redacted_text: Optional[str] = None
87
 
88
  @property
89
  def safe(self) -> bool:
90
+ """
91
+ Property to check if the text is safe (no secrets detected).
92
+
93
+ Returns:
94
+ bool: True if no secrets were detected, False otherwise.
95
+ """
96
+ return not self.contains_secrets
97
 
98
 
99
  class SecretsDetectionResponse(SecretsDetectionSimpleResponse):
100
+ """
101
+ A detailed response model for secrets detection.
102
+
103
+ Attributes:
104
+ detected_secrets (dict[str, list[str]]): Dictionary of detected secrets.
105
+ """
106
+
107
  detected_secrets: dict[str, list[str]]
108
 
109
 
110
  class SecretsDetectionGuardrail(Guardrail):
111
+ """
112
+ A guardrail for detecting secrets in text using regex patterns.
113
+ reference: SecretBench: A Dataset of Software Secrets
114
+ https://arxiv.org/abs/2303.06729
115
+
116
+ Attributes:
117
+ regex_model (RegexModel): The regex model used for detection.
118
+ patterns (Union[dict[str, str], dict[str, list[str]]]): The patterns used for detection.
119
+ redaction (REDACTION): The type of redaction to apply.
120
+ """
121
+
122
  regex_model: RegexModel
123
  patterns: Union[dict[str, str], dict[str, list[str]]] = {}
124
  redaction: REDACTION
 
129
  redaction: REDACTION = REDACTION.REDACT_ALL,
130
  **kwargs,
131
  ):
132
+ """
133
+ Initialize the SecretsDetectionGuardrail.
134
+
135
+ Args:
136
+ use_defaults (bool): Whether to use default patterns.
137
+ redaction (REDACTION): The type of redaction to apply.
138
+ **kwargs: Additional keyword arguments.
139
+ """
140
  patterns = {}
141
  if use_defaults:
142
  patterns = DEFAULT_SECRETS_PATTERNS.copy()
143
  if kwargs.get("patterns"):
144
  patterns.update(kwargs["patterns"])
145
 
 
146
  regex_model = RegexModel(patterns=patterns)
147
 
 
148
  super().__init__(
149
  regex_model=regex_model,
150
  patterns=patterns,
 
155
  def guard(
156
  self,
157
  prompt: str,
158
+ return_detected_secrets: bool = True,
159
  **kwargs,
160
  ) -> SecretsDetectionResponse | SecretsDetectionResponse:
161
  """
162
+ Check if the input prompt contains any secrets based on the regex patterns.
163
 
164
  Args:
165
+ prompt (str): Input text to check for secrets.
166
+ return_detected_secrets (bool): If True, returns detailed secrets type information.
167
 
168
  Returns:
169
+ SecretsDetectionResponse or SecretsDetectionResponse: Detection results.
170
  """
171
  result = self.regex_model.check(prompt)
172
 
 
173
  explanation_parts = []
174
  if result.matched_patterns:
175
  explanation_parts.append("Found the following secrets in the text:")
 
183
  for secret_type, matches in result.matched_patterns.items():
184
  redacted_text = redact(redacted_text, matches, self.redaction)
185
 
186
+ if return_detected_secrets:
187
  return SecretsDetectionResponse(
188
  contains_secrets=not result.passed,
189
  detected_secrets=result.matched_patterns,
 
192
  )
193
  else:
194
  return SecretsDetectionSimpleResponse(
195
+ contains_secrets=not result.passed,
196
  explanation="\n".join(explanation_parts),
197
  redacted_text=redacted_text,
198
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/guardrails_genie/guardrails/test_secrets_detection.py CHANGED
@@ -93,7 +93,7 @@ def test_secrets_detection_guardrail_detect_types(mock_secrets_guard):
93
  guardrail = SecretsDetectionGuardrail(redaction=REDACTION.REDACT_ALL)
94
  prompt = "My secret key is ABCDEFGHIJKL"
95
 
96
- result = guardrail.guard(prompt=prompt, return_detected_types=True)
97
 
98
  assert result.contains_secrets is True
99
  assert result.explanation == "The output contains secrets."
@@ -110,7 +110,7 @@ def test_secrets_detection_guardrail_simple_response(mock_secrets_guard):
110
  guardrail = SecretsDetectionGuardrail(redaction=REDACTION.REDACT_ALL)
111
  prompt = "My secret key is ABCDEFGHIJKL"
112
 
113
- result = guardrail.guard(prompt=prompt, return_detected_types=False)
114
 
115
  assert result.contains_secrets is True
116
  assert result.explanation == "The output contains secrets."
@@ -126,7 +126,7 @@ def test_secrets_detection_guardrail_no_secrets(mock_secrets_guard):
126
  guardrail = SecretsDetectionGuardrail(redaction=REDACTION.REDACT_ALL)
127
  prompt = "This is a safe text with no secrets."
128
 
129
- result = guardrail.guard(prompt=prompt, return_detected_types=True)
130
 
131
  assert result.contains_secrets is False
132
  assert result.explanation == "No secrets detected in the text."
@@ -143,7 +143,7 @@ def pattern_strategy(pattern):
143
  @given(pattern_strategy(DEFAULT_SECRETS_PATTERNS["JwtToken"][0]))
144
  def test_specific_pattern_guardrail(text):
145
  guardrail = SecretsDetectionGuardrail(redaction=REDACTION.REDACT_ALL)
146
- result = guardrail.guard(prompt=text, return_detected_types=True)
147
 
148
  assert result.contains_secrets is True
149
  assert "JwtToken" in result.detected_secrets
 
93
  guardrail = SecretsDetectionGuardrail(redaction=REDACTION.REDACT_ALL)
94
  prompt = "My secret key is ABCDEFGHIJKL"
95
 
96
+ result = guardrail.guard(prompt=prompt, return_detected_secrets=True)
97
 
98
  assert result.contains_secrets is True
99
  assert result.explanation == "The output contains secrets."
 
110
  guardrail = SecretsDetectionGuardrail(redaction=REDACTION.REDACT_ALL)
111
  prompt = "My secret key is ABCDEFGHIJKL"
112
 
113
+ result = guardrail.guard(prompt=prompt, return_detected_secrets=False)
114
 
115
  assert result.contains_secrets is True
116
  assert result.explanation == "The output contains secrets."
 
126
  guardrail = SecretsDetectionGuardrail(redaction=REDACTION.REDACT_ALL)
127
  prompt = "This is a safe text with no secrets."
128
 
129
+ result = guardrail.guard(prompt=prompt, return_detected_secrets=True)
130
 
131
  assert result.contains_secrets is False
132
  assert result.explanation == "No secrets detected in the text."
 
143
  @given(pattern_strategy(DEFAULT_SECRETS_PATTERNS["JwtToken"][0]))
144
  def test_specific_pattern_guardrail(text):
145
  guardrail = SecretsDetectionGuardrail(redaction=REDACTION.REDACT_ALL)
146
+ result = guardrail.guard(prompt=text, return_detected_secrets=True)
147
 
148
  assert result.contains_secrets is True
149
  assert "JwtToken" in result.detected_secrets