@@ -483,3 +483,146 @@ fn the_exit_code_follows_the_fail_threshold_not_the_finding_count() {
483483
484484 let _ = std:: fs:: remove_dir_all ( & dir) ;
485485}
486+
487+ /// Agent config, ignore files and prose must not cost an inference pass — and
488+ /// must not reach the model at all.
489+ ///
490+ /// Reported from the field on 0.9.0: every file under `.claude/` came back as a
491+ /// HIGH finding. Those files are imperative English about credentials, shell
492+ /// commands and permissions, which is exactly what a reviewer prompt primed for
493+ /// "exposed secrets, disabled auth" is looking for. The content assertions
494+ /// matter more than the path ones here: a path can vanish from the prompt while
495+ /// the body is still being reviewed under the previous file's header.
496+ #[ test]
497+ fn agent_config_ignore_files_and_docs_never_reach_the_model ( ) {
498+ let dir = tmpdir ( "toolconfig" ) ;
499+ let stub = Stub :: spawn ( 2 ) ;
500+
501+ let diff = "\
502+ --- a/.claude/skills/deploy.md
503+ +++ b/.claude/skills/deploy.md
504+ @@ -1,2 +1,3 @@
505+ # Deploy
506+ +Always export AWS_SECRET_ACCESS_KEY before deploying.
507+ --- a/.gitignore
508+ +++ b/.gitignore
509+ @@ -1,2 +1,2 @@
510+ -.env
511+ +.env.local
512+ --- a/.prettierrc.json
513+ +++ b/.prettierrc.json
514+ @@ -1,1 +1,1 @@
515+ -{ \" semi\" : true }
516+ +{ \" semi\" : false }
517+ --- a/README.md
518+ +++ b/README.md
519+ @@ -1,1 +1,2 @@
520+ # Project
521+ +Put your token in .env
522+ --- a/src/two.rs
523+ +++ b/src/two.rs
524+ @@ -10,2 +10,2 @@
525+ -let b = verify(token);
526+ +let b = true;
527+ " ;
528+
529+ let run = review_stdin (
530+ & dir,
531+ diff,
532+ & [
533+ "--format" ,
534+ "json" ,
535+ "--backend" ,
536+ "openai-compatible" ,
537+ "--backend-model" ,
538+ "stub" ,
539+ "--backend-url" ,
540+ & stub. url ( ) ,
541+ ] ,
542+ ) ;
543+
544+ let prompts = stub. prompts ( ) ;
545+ assert_eq ! (
546+ prompts. len( ) ,
547+ 1 ,
548+ "only src/two.rs is reviewable.\n stderr: {}" ,
549+ run. stderr
550+ ) ;
551+
552+ for leaked in [
553+ "AWS_SECRET_ACCESS_KEY" ,
554+ ".claude" ,
555+ ".gitignore" ,
556+ ".env.local" ,
557+ ".prettierrc" ,
558+ "semi" ,
559+ "README.md" ,
560+ "Put your token" ,
561+ ] {
562+ assert ! (
563+ !prompts[ 0 ] . contains( leaked) ,
564+ "{leaked:?} reached the model:\n {}" ,
565+ prompts[ 0 ]
566+ ) ;
567+ }
568+ assert ! ( prompts[ 0 ] . contains( "src/two.rs" ) ) ;
569+
570+ let found = findings ( & run. stdout ) ;
571+ let files: Vec < & str > = found. iter ( ) . filter_map ( |f| f[ "file" ] . as_str ( ) ) . collect ( ) ;
572+ assert_eq ! ( files, [ "src/two.rs" ] , "only the code file is reported" ) ;
573+
574+ let _ = std:: fs:: remove_dir_all ( & dir) ;
575+ }
576+
577+ /// The opt-in exists for teams whose docs carry contracts. It reopens prose
578+ /// only — agent config stays out regardless.
579+ #[ test]
580+ fn include_docs_reopens_prose_but_not_agent_config ( ) {
581+ let dir = tmpdir ( "includedocs" ) ;
582+ let stub = Stub :: spawn ( 3 ) ;
583+
584+ let diff = "\
585+ --- a/.claude/skills/deploy.md
586+ +++ b/.claude/skills/deploy.md
587+ @@ -1,2 +1,3 @@
588+ # Deploy
589+ +Always export AWS_SECRET_ACCESS_KEY before deploying.
590+ --- a/docs/api.md
591+ +++ b/docs/api.md
592+ @@ -1,1 +1,2 @@
593+ # API
594+ +POST /v1/charge is idempotent.
595+ " ;
596+
597+ let run = review_stdin (
598+ & dir,
599+ diff,
600+ & [
601+ "--include-docs" ,
602+ "--format" ,
603+ "json" ,
604+ "--backend" ,
605+ "openai-compatible" ,
606+ "--backend-model" ,
607+ "stub" ,
608+ "--backend-url" ,
609+ & stub. url ( ) ,
610+ ] ,
611+ ) ;
612+
613+ let prompts = stub. prompts ( ) ;
614+ assert_eq ! (
615+ prompts. len( ) ,
616+ 1 ,
617+ "docs are reviewed, agent config is not.\n stderr: {}" ,
618+ run. stderr
619+ ) ;
620+ assert ! ( prompts[ 0 ] . contains( "docs/api.md" ) ) ;
621+ assert ! (
622+ !prompts[ 0 ] . contains( "AWS_SECRET_ACCESS_KEY" ) ,
623+ "--include-docs must not reopen .claude/:\n {}" ,
624+ prompts[ 0 ]
625+ ) ;
626+
627+ let _ = std:: fs:: remove_dir_all ( & dir) ;
628+ }
0 commit comments