@@ -12,23 +12,58 @@ const PROMPT_PATH = "/tmp/gh-aw/aw-prompts/prompt.txt";
1212const PROMPT_START_MARKER = "<!-- gh-aw-comment-memory-prompt:start -->" ;
1313const PROMPT_END_MARKER = "<!-- gh-aw-comment-memory-prompt:end -->" ;
1414const MAX_SCAN_PAGES = 50 ;
15+ const MAX_SCAN_EMPTY_PAGES = 5 ;
16+ const MAX_MEMORY_ID_LENGTH = 64 ;
17+
18+ function isSafeMemoryId ( memoryId ) {
19+ if ( typeof memoryId !== "string" || memoryId . length === 0 || memoryId . length > MAX_MEMORY_ID_LENGTH ) {
20+ return false ;
21+ }
22+ if ( memoryId . includes ( ".." ) || memoryId . includes ( "/" ) || memoryId . includes ( "\\" ) ) {
23+ return false ;
24+ }
25+ return / ^ [ A - Z a - z 0 - 9 _ - ] + $ / . test ( memoryId ) ;
26+ }
1527
1628function extractCommentMemoryEntries ( commentBody ) {
1729 if ( ! commentBody || typeof commentBody !== "string" ) {
1830 return [ ] ;
1931 }
2032
21- const pattern = new RegExp ( `<${ COMMENT_MEMORY_TAG } \\s+id="([A-Za-z0-9_-]+)">([\\s\\S]*?)<\\/${ COMMENT_MEMORY_TAG } >` , "g" ) ;
22- const matches = [ ] ;
23- let match = pattern . exec ( commentBody ) ;
24- while ( match ) {
25- matches . push ( {
26- memoryId : match [ 1 ] ,
27- content : String ( match [ 2 ] || "" ) . trim ( ) ,
28- } ) ;
29- match = pattern . exec ( commentBody ) ;
33+ const entries = [ ] ;
34+ const closeTag = `</${ COMMENT_MEMORY_TAG } >` ;
35+ let cursor = 0 ;
36+ while ( cursor < commentBody . length ) {
37+ const openStart = commentBody . indexOf ( `<${ COMMENT_MEMORY_TAG } id="` , cursor ) ;
38+ if ( openStart < 0 ) {
39+ break ;
40+ }
41+
42+ const idStart = openStart + `<${ COMMENT_MEMORY_TAG } id="` . length ;
43+ const idEnd = commentBody . indexOf ( '">' , idStart ) ;
44+ if ( idEnd < 0 ) {
45+ break ;
46+ }
47+
48+ const memoryId = commentBody . slice ( idStart , idEnd ) ;
49+ const contentStart = idEnd + 2 ;
50+ const closeStart = commentBody . indexOf ( closeTag , contentStart ) ;
51+ if ( closeStart < 0 ) {
52+ break ;
53+ }
54+
55+ if ( isSafeMemoryId ( memoryId ) ) {
56+ entries . push ( {
57+ memoryId,
58+ content : String ( commentBody . slice ( contentStart , closeStart ) || "" ) . trim ( ) ,
59+ } ) ;
60+ } else {
61+ core . warning ( `comment_memory setup: skipping unsafe memory_id '${ memoryId } ' found in managed comment` ) ;
62+ }
63+
64+ cursor = closeStart + closeTag . length ;
3065 }
31- return matches ;
66+ return entries ;
3267}
3368
3469function loadSafeOutputsConfig ( ) {
@@ -90,6 +125,7 @@ async function collectCommentMemoryFiles(githubClient, commentMemoryConfig) {
90125
91126 core . info ( `comment_memory setup: loading managed comment memory from ${ targetRepo . slug } #${ targetNumber } ` ) ;
92127 const memoryMap = new Map ( ) ;
128+ let emptyPageCount = 0 ;
93129
94130 for ( let page = 1 ; page <= MAX_SCAN_PAGES ; page ++ ) {
95131 const { data } = await githubClient . rest . issues . listComments ( {
@@ -104,13 +140,27 @@ async function collectCommentMemoryFiles(githubClient, commentMemoryConfig) {
104140 break ;
105141 }
106142
143+ let pageAddedEntries = 0 ;
107144 for ( const comment of data ) {
108145 const entries = extractCommentMemoryEntries ( comment . body ) ;
109146 for ( const entry of entries ) {
147+ if ( ! memoryMap . has ( entry . memoryId ) || memoryMap . get ( entry . memoryId ) !== entry . content ) {
148+ pageAddedEntries ++ ;
149+ }
110150 memoryMap . set ( entry . memoryId , entry . content ) ;
111151 }
112152 }
113153
154+ if ( pageAddedEntries === 0 ) {
155+ emptyPageCount ++ ;
156+ if ( emptyPageCount >= MAX_SCAN_EMPTY_PAGES ) {
157+ core . info ( `comment_memory setup: stopping scan after ${ emptyPageCount } pages without new memory entries` ) ;
158+ break ;
159+ }
160+ } else {
161+ emptyPageCount = 0 ;
162+ }
163+
114164 if ( data . length < 100 ) {
115165 break ;
116166 }
@@ -119,6 +169,10 @@ async function collectCommentMemoryFiles(githubClient, commentMemoryConfig) {
119169 fs . mkdirSync ( COMMENT_MEMORY_DIR , { recursive : true } ) ;
120170 const writtenFiles = [ ] ;
121171 for ( const [ memoryId , content ] of memoryMap . entries ( ) ) {
172+ if ( ! isSafeMemoryId ( memoryId ) ) {
173+ core . warning ( `comment_memory setup: skipping unsafe memory_id '${ memoryId } ' while writing files` ) ;
174+ continue ;
175+ }
122176 const filePath = path . join ( COMMENT_MEMORY_DIR , `${ memoryId } .md` ) ;
123177 fs . writeFileSync ( filePath , `${ content } \n` ) ;
124178 writtenFiles . push ( filePath ) ;
0 commit comments