@@ -306,10 +306,7 @@ function checkPromptSafety(candidateFiles) {
306306 const content = readFileSync ( fullPath , "utf8" ) ;
307307 checkPromptUnicode ( normalizedFile , content ) ;
308308 checkPromptUnsafeDeveloperCommands ( normalizedFile , content ) ;
309-
310- if ( isAgentInstructionFile ( normalizedFile ) ) {
311- checkAgentInstructionExfiltration ( normalizedFile , content ) ;
312- }
309+ checkPromptCredentialExfiltration ( normalizedFile , content ) ;
313310 }
314311}
315312
@@ -512,8 +509,12 @@ function isInvisibleUnicode(codePoint) {
512509 ) ;
513510}
514511
515- function checkAgentInstructionExfiltration ( file , content ) {
516- if ( ! hasNetworkEgressPattern ( content ) || ! hasSensitiveCredentialPattern ( content ) ) {
512+ function checkPromptCredentialExfiltration ( file , content ) {
513+ const hasCredentialExfiltration = isAgentInstructionFile ( file )
514+ ? hasNetworkEgressPattern ( content ) && hasSensitiveCredentialPattern ( content )
515+ : hasRuleFileCredentialExfiltrationPattern ( content ) ;
516+
517+ if ( ! hasCredentialExfiltration ) {
517518 return ;
518519 }
519520
@@ -527,6 +528,47 @@ function checkAgentInstructionExfiltration(file, content) {
527528 } ) ;
528529}
529530
531+ function hasRuleFileCredentialExfiltrationPattern ( content ) {
532+ return extractRuleInstructionContexts ( normalizeShellContinuations ( content ) ) . some (
533+ ( context ) =>
534+ hasNetworkEgressPattern ( context ) &&
535+ hasSensitiveCredentialPattern ( context ) &&
536+ hasCredentialTransferIntentPattern ( context ) ,
537+ ) ;
538+ }
539+
540+ function extractRuleInstructionContexts ( content ) {
541+ const lines = content
542+ . split ( / \r ? \n / )
543+ . map ( ( line ) => line . trim ( ) )
544+ . filter ( Boolean ) ;
545+ const contexts = [ ...lines ] ;
546+
547+ for ( let index = 0 ; index < lines . length ; index += 1 ) {
548+ contexts . push ( lines . slice ( index , index + 3 ) . join ( " " ) ) ;
549+ }
550+
551+ return contexts ;
552+ }
553+
554+ function hasCredentialTransferIntentPattern ( content ) {
555+ if (
556+ / \b (?: c u r l | w g e t | n c | n c a t | n e t c a t | s c p | s f t p | f t p ) \b | \b I n v o k e - W e b R e q u e s t \b | \b i w r \b | \b (?: s e n d | u p l o a d | p o s t | s u b m i t | t r a n s m i t | e x f i l t r a t | l e a k ) \b / i. test (
557+ content ,
558+ )
559+ ) {
560+ return true ;
561+ }
562+
563+ return / (?: f e t c h | X M L H t t p R e q u e s t ) \s * \( / i. test ( content ) && hasHighRiskLocalCredentialPattern ( content ) ;
564+ }
565+
566+ function hasHighRiskLocalCredentialPattern ( content ) {
567+ return / (?: ^ | [ ^ \w ] ) (?: ~ \/ ) ? \. s s h \/ (?: i d _ r s a | i d _ e d 2 5 5 1 9 | c o n f i g ) | (?: ^ | [ ^ \w ] ) (?: ~ \/ ) ? \. a w s \/ (?: c r e d e n t i a l s | c o n f i g ) | (?: ^ | [ ^ \w ] ) (?: ~ \/ ) ? \. c o n f i g \/ g h | (?: ^ | [ ^ \w ] ) \. n p m r c \b | (?: ^ | [ ^ \w ] ) \. p y p i r c \b | c a r g o \/ c r e d e n t i a l s | w a l l e t \. d a t | (?: ^ | [ ^ \w ] ) \. e n v \b | \b p r o c e s s \. e n v \. (?: G I T H U B _ T O K E N | G H _ T O K E N | N P M _ T O K E N | N O D E _ A U T H _ T O K E N | O P E N A I _ A P I _ K E Y | A N T H R O P I C _ A P I _ K E Y | S T R I P E _ S E C R E T _ K E Y | A W S _ [ A - Z 0 - 9 _ ] * (?: S E C R E T | K E Y | T O K E N ) [ A - Z 0 - 9 _ ] * ) \b | \b p r i n t e n v \b | \b g h a u t h t o k e n \b / i. test (
568+ content ,
569+ ) ;
570+ }
571+
530572function hasNetworkEgressPattern ( content ) {
531573 return / \b (?: c u r l | w g e t | n c | n c a t | n e t c a t | s c p | s f t p | f t p ) \b | (?: f e t c h | X M L H t t p R e q u e s t ) \s * \( | \b I n v o k e - W e b R e q u e s t \b | \b i w r \b | h t t p s ? : \/ \/ / i. test (
532574 content ,
@@ -541,6 +583,7 @@ function hasSensitiveCredentialPattern(content) {
541583}
542584
543585function checkPromptUnsafeDeveloperCommands ( file , content ) {
586+ const commandContent = normalizeShellContinuations ( content ) ;
544587 const unsafeCommandChecks = [
545588 {
546589 ruleId : "prompt/no-secret-read-command" ,
@@ -626,7 +669,7 @@ function checkPromptUnsafeDeveloperCommands(file, content) {
626669 ] ;
627670
628671 for ( const check of unsafeCommandChecks ) {
629- if ( ! check . pattern . test ( content ) ) continue ;
672+ if ( ! check . pattern . test ( commandContent ) ) continue ;
630673
631674 addFailure ( {
632675 ruleId : check . ruleId ,
@@ -639,6 +682,10 @@ function checkPromptUnsafeDeveloperCommands(file, content) {
639682 }
640683}
641684
685+ function normalizeShellContinuations ( content ) {
686+ return content . replace ( / \\ \r ? \n [ \t ] * / g, " " ) ;
687+ }
688+
642689function formatCodePoint ( codePoint ) {
643690 return `U+${ codePoint . toString ( 16 ) . toUpperCase ( ) . padStart ( 4 , "0" ) } ` ;
644691}
0 commit comments