2323import java .util .Objects ;
2424import java .util .Optional ;
2525import java .util .concurrent .atomic .AtomicInteger ;
26- import java .util .stream .Collectors ;
27- import java .util .stream .Stream ;
2826import javax .inject .Inject ;
2927
3028/** A codemod that removes any sensitive data being logged. */
@@ -51,6 +49,12 @@ public SensitiveDataLoggingCodemod(
5149 public CodemodFileScanningResult visit (
5250 final CodemodInvocationContext context , final CompilationUnit cu ) {
5351 final var source = context .path ();
52+ final List <String > numberedLines ;
53+ try {
54+ numberedLines = readNumberedLines (source );
55+ } catch (IOException e ) {
56+ throw new UncheckedIOException ("Couldn't read source file" , e );
57+ }
5458 final List <Result > results = sarif .getResultsByLocationPath (source );
5559 final List <CodemodChange > changes = new ArrayList <>();
5660 for (final Result result : results ) {
@@ -63,7 +67,7 @@ public CodemodFileScanningResult visit(
6367
6468 SensitivityAndFixAnalysis analysis ;
6569 try {
66- analysis = performSensitivityAnalysis (source , startLine );
70+ analysis = performSensitivityAnalysis (numberedLines , startLine );
6771 } catch (IOException e ) {
6872 throw new UncheckedIOException ("Couldn't perform sensitivity analysis" , e );
6973 }
@@ -83,8 +87,8 @@ public CodemodFileScanningResult visit(
8387 }
8488
8589 private SensitivityAndFixAnalysis performSensitivityAnalysis (
86- final Path source , final Integer startLine ) throws IOException {
87- String codeSnippet = numberedContextWithExtraLines (source , startLine );
90+ final List < String > source , final Integer startLine ) throws IOException {
91+ String codeSnippet = snippet (source , startLine );
8892 String prompt =
8993 """
9094 A tool has cited line %d of the code for possibly logging sensitive data:
@@ -145,6 +149,7 @@ private interface SensitivityAndFixAnalysis {
145149 }
146150
147151 private static class SensitivityAndFixAnalysisDTO implements SensitivityAndFixAnalysis {
152+
148153 @ JsonProperty ("sensitive_analysis_text" )
149154 private String sensitiveAnalysisText ;
150155
@@ -173,25 +178,34 @@ public String newStatement() {
173178 }
174179 }
175180
176- private static String numberedContextWithExtraLines (final Path path , final int line )
177- throws IOException {
178- int startLine = Math .max (0 , line - CONTEXT );
179- try (final Stream <String > lines = Files .lines (path )) {
180- final AtomicInteger counter = new AtomicInteger (startLine );
181- return lines
182- .skip (startLine )
183- .limit (1L + CONTEXT )
184- .map (s -> counter .incrementAndGet () + ": " + s )
185- .collect (Collectors .joining ("\n " ));
186- }
187- }
188-
189181 @ Override
190182 public boolean shouldRun () {
191183 List <Run > runs = sarif .rawDocument ().getRuns ();
192184 return runs != null && !runs .isEmpty () && !runs .get (0 ).getResults ().isEmpty ();
193185 }
194186
187+ /** Reads the source code from the given file and numbers each line. */
188+ private List <String > readNumberedLines (final Path source ) throws IOException {
189+ final var counter = new AtomicInteger ();
190+ try (final var lines = Files .lines (source )) {
191+ return lines .map (line -> counter .incrementAndGet () + ": " + line ).toList ();
192+ }
193+ }
194+
195+ /**
196+ * Returns a snippet of code surrounding the given line number.
197+ *
198+ * @param lines numbered source code lines
199+ * @param line the line number to center the snippet around
200+ * @return a snippet of code surrounding the given line number
201+ */
202+ private static String snippet (final List <String > lines , final int line ) {
203+ final int start = Math .max (0 , line - CONTEXT );
204+ final int end = Math .min (lines .size (), line + CONTEXT + 1 );
205+ final var snippet = lines .subList (start , end );
206+ return String .join ("\n " , snippet );
207+ }
208+
195209 /**
196210 * Number of lines of leading and trailing context surrounding each Semgrep finding to include in
197211 * the code snippet sent to OpenAI.
0 commit comments