@@ -8,7 +8,7 @@ use pyo3_stub_gen::derive::*;
88
99use crate :: config:: SecretsDetectionConfig ;
1010use crate :: object_model:: copy_object_with_updates;
11- use crate :: scanner:: scan_container;
11+ use crate :: scanner:: { scan_container, scan_container_findings } ;
1212
1313#[ gen_stub_pyclass]
1414#[ pyclass]
@@ -32,54 +32,28 @@ impl SecretsDetectionPluginCore {
3232 payload : & Bound < ' _ , PyAny > ,
3333 _context : & Bound < ' _ , PyAny > ,
3434 ) -> PyResult < Py < PyAny > > {
35- let args = payload. getattr ( "args" ) ?;
36- let ( count, redacted_args, findings) = scan_container ( py, & args, & self . config ) ?;
37- if self . should_block ( count) {
38- let modified_payload = if self . config . redact && count > 0 {
39- copy_with_update ( py, payload, [ ( "args" , redacted_args. clone ( ) . unbind ( ) ) ] ) ?
40- } else {
41- payload. clone ( ) . unbind ( )
42- } ;
43- return blocked_result (
44- py,
45- "PromptPrehookResult" ,
46- "Potential secrets detected in prompt arguments" ,
47- count,
48- findings. as_any ( ) ,
49- modified_payload,
50- ) ;
51- }
52-
53- if self . config . redact && count > 0 {
54- let modified_payload =
55- copy_with_update ( py, payload, [ ( "args" , redacted_args. unbind ( ) ) ] ) ?;
56- return build_framework_object (
57- py,
58- "PromptPrehookResult" ,
59- [
60- ( "modified_payload" , modified_payload) ,
61- (
62- "metadata" ,
63- redaction_metadata ( py, count) ?. into_any ( ) . unbind ( ) ,
64- ) ,
65- ] ,
66- ) ;
67- }
68-
69- if count > 0 {
70- return build_framework_object (
71- py,
72- "PromptPrehookResult" ,
73- [ (
74- "metadata" ,
75- findings_metadata ( py, count, findings. as_any ( ) ) ?
76- . into_any ( )
77- . unbind ( ) ,
78- ) ] ,
79- ) ;
80- }
35+ self . scan_payload_attr (
36+ py,
37+ payload,
38+ "args" ,
39+ "PromptPrehookResult" ,
40+ "Potential secrets detected in prompt arguments" ,
41+ )
42+ }
8143
82- default_result ( py, "PromptPrehookResult" )
44+ pub fn tool_pre_invoke (
45+ & self ,
46+ py : Python < ' _ > ,
47+ payload : & Bound < ' _ , PyAny > ,
48+ _context : & Bound < ' _ , PyAny > ,
49+ ) -> PyResult < Py < PyAny > > {
50+ self . scan_payload_attr (
51+ py,
52+ payload,
53+ "args" ,
54+ "ToolPreInvokeResult" ,
55+ "Potential secrets detected in tool arguments" ,
56+ )
8357 }
8458
8559 pub fn tool_post_invoke (
@@ -88,30 +62,51 @@ impl SecretsDetectionPluginCore {
8862 payload : & Bound < ' _ , PyAny > ,
8963 _context : & Bound < ' _ , PyAny > ,
9064 ) -> PyResult < Py < PyAny > > {
91- let value = payload. getattr ( "result" ) ?;
92- let ( count, redacted_result, findings) = scan_container ( py, & value, & self . config ) ?;
65+ self . scan_payload_attr (
66+ py,
67+ payload,
68+ "result" ,
69+ "ToolPostInvokeResult" ,
70+ "Potential secrets detected in tool result" ,
71+ )
72+ }
73+
74+ pub fn resource_post_fetch (
75+ & self ,
76+ py : Python < ' _ > ,
77+ payload : & Bound < ' _ , PyAny > ,
78+ _context : & Bound < ' _ , PyAny > ,
79+ ) -> PyResult < Py < PyAny > > {
80+ let content = payload. getattr ( "content" ) ?;
81+ let Ok ( text) = content. getattr ( "text" ) else {
82+ return default_result ( py, "ResourcePostFetchResult" ) ;
83+ } ;
84+ let ( count, redacted_text, findings) = scan_container ( py, & text, & self . config ) ?;
9385 if self . should_block ( count) {
9486 let modified_payload = if self . config . redact && count > 0 {
95- copy_with_update ( py, payload, [ ( "result" , redacted_result. clone ( ) . unbind ( ) ) ] ) ?
87+ let modified_content =
88+ copy_with_update ( py, & content, [ ( "text" , redacted_text. clone ( ) . unbind ( ) ) ] ) ?;
89+ copy_with_update ( py, payload, [ ( "content" , modified_content) ] ) ?
9690 } else {
9791 payload. clone ( ) . unbind ( )
9892 } ;
9993 return blocked_result (
10094 py,
101- "ToolPostInvokeResult " ,
102- "Potential secrets detected in tool result " ,
95+ "ResourcePostFetchResult " ,
96+ "Potential secrets detected in resource content " ,
10397 count,
10498 findings. as_any ( ) ,
10599 modified_payload,
106100 ) ;
107101 }
108102
109103 if self . config . redact && count > 0 {
110- let modified_payload =
111- copy_with_update ( py, payload, [ ( "result" , redacted_result. unbind ( ) ) ] ) ?;
104+ let modified_content =
105+ copy_with_update ( py, & content, [ ( "text" , redacted_text. unbind ( ) ) ] ) ?;
106+ let modified_payload = copy_with_update ( py, payload, [ ( "content" , modified_content) ] ) ?;
112107 return build_framework_object (
113108 py,
114- "ToolPostInvokeResult " ,
109+ "ResourcePostFetchResult " ,
115110 [
116111 ( "modified_payload" , modified_payload) ,
117112 (
@@ -125,7 +120,7 @@ impl SecretsDetectionPluginCore {
125120 if count > 0 {
126121 return build_framework_object (
127122 py,
128- "ToolPostInvokeResult " ,
123+ "ResourcePostFetchResult " ,
129124 [ (
130125 "metadata" ,
131126 findings_metadata ( py, count, findings. as_any ( ) ) ?
@@ -135,45 +130,58 @@ impl SecretsDetectionPluginCore {
135130 ) ;
136131 }
137132
138- default_result ( py, "ToolPostInvokeResult " )
133+ default_result ( py, "ResourcePostFetchResult " )
139134 }
135+ }
140136
141- pub fn resource_post_fetch (
137+ impl SecretsDetectionPluginCore {
138+ fn should_block ( & self , count : usize ) -> bool {
139+ self . config . block_on_detection && count >= self . config . min_findings_to_block
140+ }
141+
142+ fn scan_payload_attr (
142143 & self ,
143144 py : Python < ' _ > ,
144145 payload : & Bound < ' _ , PyAny > ,
145- _context : & Bound < ' _ , PyAny > ,
146+ attr : & str ,
147+ result_class : & str ,
148+ block_description : & str ,
146149 ) -> PyResult < Py < PyAny > > {
147- let content = payload. getattr ( "content" ) ?;
148- let Ok ( text) = content. getattr ( "text" ) else {
149- return default_result ( py, "ResourcePostFetchResult" ) ;
150+ let value = payload. getattr ( attr) ?;
151+ let ( mut count, mut findings) = scan_container_findings ( py, & value, & self . config ) ?;
152+ let redacted_value = if self . config . redact && count > 0 {
153+ let ( redacted_count, redacted, redacted_findings) =
154+ scan_container ( py, & value, & self . config ) ?;
155+ count = redacted_count;
156+ findings = redacted_findings;
157+ Some ( redacted)
158+ } else {
159+ None
150160 } ;
151- let ( count , redacted_text , findings ) = scan_container ( py , & text , & self . config ) ? ;
161+
152162 if self . should_block ( count) {
153163 let modified_payload = if self . config . redact && count > 0 {
154- let modified_content =
155- copy_with_update ( py, & content, [ ( "text" , redacted_text. clone ( ) . unbind ( ) ) ] ) ?;
156- copy_with_update ( py, payload, [ ( "content" , modified_content) ] ) ?
164+ let redacted = redacted_value. expect ( "redacted value exists" ) ;
165+ copy_with_update ( py, payload, [ ( attr, redacted. unbind ( ) ) ] ) ?
157166 } else {
158167 payload. clone ( ) . unbind ( )
159168 } ;
160169 return blocked_result (
161170 py,
162- "ResourcePostFetchResult" ,
163- "Potential secrets detected in resource content" ,
171+ result_class ,
172+ block_description ,
164173 count,
165174 findings. as_any ( ) ,
166175 modified_payload,
167176 ) ;
168177 }
169178
170179 if self . config . redact && count > 0 {
171- let modified_content =
172- copy_with_update ( py, & content, [ ( "text" , redacted_text. unbind ( ) ) ] ) ?;
173- let modified_payload = copy_with_update ( py, payload, [ ( "content" , modified_content) ] ) ?;
180+ let redacted = redacted_value. expect ( "redacted value exists" ) ;
181+ let modified_payload = copy_with_update ( py, payload, [ ( attr, redacted. unbind ( ) ) ] ) ?;
174182 return build_framework_object (
175183 py,
176- "ResourcePostFetchResult" ,
184+ result_class ,
177185 [
178186 ( "modified_payload" , modified_payload) ,
179187 (
@@ -187,7 +195,7 @@ impl SecretsDetectionPluginCore {
187195 if count > 0 {
188196 return build_framework_object (
189197 py,
190- "ResourcePostFetchResult" ,
198+ result_class ,
191199 [ (
192200 "metadata" ,
193201 findings_metadata ( py, count, findings. as_any ( ) ) ?
@@ -197,13 +205,7 @@ impl SecretsDetectionPluginCore {
197205 ) ;
198206 }
199207
200- default_result ( py, "ResourcePostFetchResult" )
201- }
202- }
203-
204- impl SecretsDetectionPluginCore {
205- fn should_block ( & self , count : usize ) -> bool {
206- self . config . block_on_detection && count >= self . config . min_findings_to_block
208+ default_result ( py, result_class)
207209 }
208210}
209211
0 commit comments