@@ -29,6 +29,29 @@ function parseModelRef(ref: string): { provider?: string; model?: string } {
2929 } ;
3030}
3131
32+ /**
33+ * Defense against the classic accident: pasting an API key into config.json
34+ * (which is safe to commit) and pushing it. Unknown to the schema by design —
35+ * keys come from environment variables — so any secret-looking field is a mistake.
36+ */
37+ const SECRET_FIELD_RE = / ^ ( .* (?: a p i [ _ - ] ? k e y | a p i k e y | s e c r e t | t o k e n | p a s s w o r d | c r e d e n t i a l ) .* | s k - .* ) $ / i;
38+
39+ function findSecretLikeFields ( value : unknown , prefix = "" ) : string [ ] {
40+ const hits : string [ ] = [ ] ;
41+ if ( Array . isArray ( value ) ) return hits ;
42+ if ( value !== null && typeof value === "object" ) {
43+ for ( const [ key , nested ] of Object . entries ( value as Record < string , unknown > ) ) {
44+ const field = prefix ? `${ prefix } .${ key } ` : key ;
45+ if ( SECRET_FIELD_RE . test ( key ) || ( typeof nested === "string" && / ^ s k - [ A - Z a - z 0 - 9 ] / . test ( nested ) ) ) {
46+ hits . push ( field ) ;
47+ } else {
48+ hits . push ( ...findSecretLikeFields ( nested , field ) ) ;
49+ }
50+ }
51+ }
52+ return hits ;
53+ }
54+
3255export interface LoadedConfig {
3356 config : TinyCodeConfig ;
3457 /** Non-fatal problems: unreadable file, schema violations of unknown shape. */
@@ -47,7 +70,15 @@ export function loadConfig(projectRoot: string): LoadedConfig {
4770 const file = path . join ( projectRoot , ".tinycode" , "config.json" ) ;
4871 try {
4972 const raw = readFileSync ( file , "utf8" ) ;
50- const parsed = configSchema . safeParse ( JSON . parse ( raw ) ) ;
73+ const json : unknown = JSON . parse ( raw ) ;
74+ const secretFields = findSecretLikeFields ( json ) ;
75+ if ( secretFields . length > 0 ) {
76+ warnings . push (
77+ `${ file } contains field(s) ${ secretFields . map ( ( f ) => `"${ f } "` ) . join ( ", " ) } that look like API keys. ` +
78+ `Keys are read from environment variables only; this file may be committed — remove secrets from it.` ,
79+ ) ;
80+ }
81+ const parsed = configSchema . safeParse ( json ) ;
5182 if ( parsed . success ) {
5283 config = parsed . data ;
5384 } else {
0 commit comments