1+ const fs = require ( 'fs' ) ;
2+ const csv = require ( 'csv-parser' ) ;
3+ const XLSX = require ( 'xlsx' ) ;
4+ const path = require ( 'path' ) ;
5+ const Step = require ( '../../actions' ) . Step ;
6+ const config = require ( '../../../config' ) ;
7+
8+ // const { exec: getDiffExec } = require('./getDiff');
9+ // Function to check for sensitive data patterns
10+ const commitConfig = config . getCommitConfig ( ) ;
11+ const checkForSensitiveData = ( cell ) => {
12+ const sensitivePatterns = [
13+ / \d { 3 } - \d { 2 } - \d { 4 } / , // Social Security Number (SSN)
14+ / \b \d { 16 } \b / , // Credit card numbers
15+ / \b \d { 5 } - \d { 4 } \b / , // ZIP+4 codes
16+ // Add more patterns as needed
17+ ] ;
18+ return sensitivePatterns . some ( pattern => {
19+ if ( pattern . test ( String ( cell ) ) ) {
20+ console . log ( `\x1b[31mDetected sensitive data: ${ cell } \x1b[0m` ) ; // Log the detected sensitive data in red
21+ return true ;
22+ }
23+ return false ;
24+ } ) ;
25+ } ;
26+ // Function to process CSV files
27+ const processCSV = async ( filePath ) => {
28+ return new Promise ( ( resolve , reject ) => {
29+ let sensitiveDataFound = false ;
30+ fs . createReadStream ( filePath )
31+ . pipe ( csv ( ) )
32+ . on ( 'data' , ( row ) => {
33+ for ( const [ key , value ] of Object . entries ( row ) ) {
34+ if ( checkForSensitiveData ( value ) ) {
35+ console . log ( `\x1b[33mSensitive data found in CSV: ${ key } : ${ value } \x1b[0m` ) ; // Log in yellow
36+ sensitiveDataFound = true ;
37+ }
38+ }
39+ } )
40+ . on ( 'end' , ( ) => {
41+ if ( ! sensitiveDataFound ) {
42+ console . log ( 'No sensitive data found in CSV.' ) ;
43+ }
44+ resolve ( sensitiveDataFound ) ; // Resolve with the flag indicating if sensitive data was found
45+ } )
46+ . on ( 'error' , ( err ) => {
47+ console . error ( `Error reading CSV file: ${ err . message } ` ) ;
48+ reject ( err ) ; // Reject the promise on error
49+ } ) ;
50+ } ) ;
51+ } ;
52+ // Function to process XLSX files
53+ const processXLSX = async ( filePath ) => {
54+ return new Promise ( ( resolve , reject ) => {
55+ let sensitiveDataFound = false ;
56+ try {
57+ const workbook = XLSX . readFile ( filePath ) ;
58+ const sheetName = workbook . SheetNames [ 0 ] ;
59+ const sheet = workbook . Sheets [ sheetName ] ;
60+ const jsonData = XLSX . utils . sheet_to_json ( sheet ) ;
61+ jsonData . forEach ( ( row ) => {
62+ for ( const [ key , value ] of Object . entries ( row ) ) {
63+ if ( checkForSensitiveData ( value ) ) {
64+ console . log ( `\x1b[33mSensitive data found in XLSX: ${ key } : ${ value } \x1b[0m` ) ; // Log in yellow
65+ sensitiveDataFound = true ;
66+ }
67+ }
68+ } ) ;
69+ if ( ! sensitiveDataFound ) {
70+ console . log ( 'No sensitive data found in XLSX.' ) ;
71+ }
72+ resolve ( sensitiveDataFound ) ; // Resolve with the flag indicating if sensitive data was found
73+ } catch ( error ) {
74+ console . error ( `Error reading XLSX file: ${ error . message } ` ) ;
75+ reject ( error ) ; // Reject the promise on error
76+ }
77+ } ) ;
78+ } ;
79+ // Function to check for sensitive data in .log and .json files
80+ const checkLogJsonFiles = async ( filePath ) => {
81+ return new Promise ( ( resolve , reject ) => {
82+ let sensitiveDataFound = false ;
83+ fs . readFile ( filePath , 'utf8' , ( err , data ) => {
84+ if ( err ) {
85+ console . error ( `Error reading file ${ filePath } : ${ err . message } ` ) ;
86+ return reject ( err ) ;
87+ }
88+ if ( checkForSensitiveData ( data ) ) {
89+ console . log ( `\x1b[Sensitive data found in ${ filePath } \x1b[0m` ) ;
90+ sensitiveDataFound = true ;
91+ }
92+ resolve ( sensitiveDataFound ) ;
93+ } ) ;
94+ } ) ;
95+ } ;
96+ // Function to parse the file based on its extension
97+ const parseFile = async ( filePath ) => {
98+
99+ const ext = path . extname ( filePath ) . toLowerCase ( ) ;
100+ const FilestoCheck = commitConfig . diff . block . proxyFileTypes ;
101+ if ( ! FilestoCheck . includes ( ext ) ) {
102+
103+ console . log ( `${ ext } should be included in CommitConfig for proxy Check!` ) ;
104+ return false ;
105+ }
106+
107+ switch ( ext ) {
108+ case '.csv' :
109+ return await processCSV ( filePath ) ;
110+ case '.xlsx' :
111+ return await processXLSX ( filePath ) ;
112+ case '.log' :
113+ return await checkLogJsonFiles ( filePath ) ;
114+ case '.json' :
115+ return await checkLogJsonFiles ( filePath ) ;
116+ default :
117+ // Skip unsupported file types without logging
118+ return false ; // Indicate that no sensitive data was found for unsupported types
119+ }
120+ } ;
121+ // Async exec function to handle actions
122+ // Function to parse file paths from git diff content
123+ const extractFilePathsFromDiff = ( diffContent ) => {
124+ const filePaths = [ ] ;
125+ const lines = diffContent . split ( '\n' ) ;
126+
127+ lines . forEach ( line => {
128+ const match = line . match ( / ^ d i f f - - g i t a \/ ( .+ ?) b \/ ( .+ ?) $ / ) ;
129+ if ( match ) {
130+ filePaths . push ( match [ 1 ] ) ; // Extract the file path from "a/" in the diff line
131+ }
132+ } ) ;
133+
134+ return filePaths ;
135+ } ;
136+
137+ const exec = async ( req , action ) => {
138+ const diffStep = action . steps . find ( ( s ) => s . stepName === 'diff' ) ;
139+ const step = new Step ( 'checksensitiveData' ) ;
140+
141+ if ( diffStep && diffStep . content ) {
142+ console . log ( 'Diff content:' , diffStep . content ) ;
143+
144+ // Use the parsing function to get file paths
145+ const filePaths = extractFilePathsFromDiff ( diffStep . content ) ;
146+
147+ if ( filePaths . length > 0 ) {
148+ // Check for sensitive data in all files
149+ const sensitiveDataFound = await Promise . all ( filePaths . map ( parseFile ) ) ;
150+ const anySensitiveDataDetected = sensitiveDataFound . some ( found => found ) ;
151+
152+ if ( anySensitiveDataDetected ) {
153+ step . blocked = true ;
154+ step . error = true ;
155+ step . errorMessage = 'Your push has been blocked due to sensitive data detection.' ;
156+ console . log ( step . errorMessage ) ;
157+ }
158+ } else {
159+ console . log ( 'No file paths provided in the diff step.' ) ;
160+ }
161+ } else {
162+ console . log ( 'No diff content available.' ) ;
163+ }
164+ action . addStep ( step ) ;
165+ return action ; // Returning action for testing purposes
166+ } ;
167+
168+
169+
170+ exec . displayName = 'logFileChanges.exec' ;
171+ exports . exec = exec ;
0 commit comments