1
1
const Step = require ( '../../actions' ) . Step ;
2
2
const config = require ( '../../../config' ) ;
3
+ const parseDiff = require ( 'parse-diff' )
3
4
4
5
const commitConfig = config . getCommitConfig ( ) ;
5
6
const privateOrganizations = config . getPrivateOrganizations ( ) ;
6
7
7
- const isDiffLegal = ( diff , organization ) => {
8
+ const BLOCK_TYPE = {
9
+ LITERAL : 'Offending Literal' ,
10
+ PATTERN : 'Offending Pattern' ,
11
+ PROVIDER : 'PROVIDER'
12
+ }
13
+
14
+
15
+ const getDiffViolations = ( diff , organization ) => {
8
16
// Commit diff is empty, i.e. '', null or undefined
9
17
if ( ! diff ) {
10
18
console . log ( 'No commit diff...' ) ;
11
- return false ;
19
+ return 'No commit diff...' ;
12
20
}
13
21
14
22
// Validation for configured block pattern(s) check...
15
23
if ( typeof diff !== 'string' ) {
16
24
console . log ( 'A non-string value has been captured for the commit diff...' ) ;
17
- return false ;
25
+ return 'A non-string value has been captured for the commit diff...' ;
18
26
}
19
27
20
- // Configured blocked literals
21
- const blockedLiterals = commitConfig . diff . block . literals ;
22
-
23
- // Configured blocked patterns
24
- const blockedPatterns = commitConfig . diff . block . patterns ;
25
-
26
- // Configured blocked providers
27
- const blockedProviders = Object . values ( commitConfig . diff . block . providers ) ;
28
-
29
- // Find all instances of blocked literals in diff...
30
- const positiveLiterals = blockedLiterals . map ( ( literal ) =>
31
- diff . toLowerCase ( ) . includes ( literal . toLowerCase ( ) ) ,
32
- ) ;
33
-
34
- // Find all instances of blocked patterns in diff...
35
- const positivePatterns = blockedPatterns . map ( ( pattern ) => diff . match ( new RegExp ( pattern , 'gi' ) ) ) ;
36
-
37
- // Find all instances of blocked providers in diff...
38
- const positiveProviders = blockedProviders . map ( ( pattern ) =>
39
- diff . match ( new RegExp ( pattern , 'gi' ) ) ,
40
- ) ;
41
-
42
- console . log ( { positiveLiterals } ) ;
43
- console . log ( { positivePatterns } ) ;
44
- console . log ( { positiveProviders } ) ;
45
-
46
- // Flatten any positive literal results into a 1D array...
47
- const literalMatches = positiveLiterals . flat ( ) . filter ( ( result ) => ! ! result ) ;
28
+ const parsedDiff = parseDiff ( diff ) ;
29
+ const combinedMatches = combineMatches ( organization ) ;
48
30
49
- // Flatten any positive pattern results into a 1D array...
50
- const patternMatches = positivePatterns . flat ( ) . filter ( ( result ) => ! ! result ) ;
51
-
52
- // Flatten any positive pattern results into a 1D array...
53
- const providerMatches =
54
- organization && privateOrganizations . includes ( organization ) // Return empty results for private organizations
55
- ? [ ]
56
- : positiveProviders . flat ( ) . filter ( ( result ) => ! ! result ) ;
57
-
58
- console . log ( { literalMatches } ) ;
59
- console . log ( { patternMatches } ) ;
60
- console . log ( { providerMatches } ) ;
61
31
32
+ const res = collectMatches ( parsedDiff , combinedMatches ) ;
62
33
// Diff matches configured block pattern(s)
63
- if ( literalMatches . length || patternMatches . length || providerMatches . length ) {
34
+ if ( res . length > 0 ) {
64
35
console . log ( 'Diff is blocked via configured literals/patterns/providers...' ) ;
65
- return false ;
36
+ // combining matches with file and line number
37
+ return res
66
38
}
67
39
68
- return true ;
40
+ return null ;
69
41
} ;
70
42
43
+ const combineMatches = ( organization ) => {
44
+
45
+ // Configured blocked literals
46
+ const blockedLiterals = commitConfig . diff . block . literals ;
47
+
48
+ // Configured blocked patterns
49
+ const blockedPatterns = commitConfig . diff . block . patterns ;
50
+
51
+ // Configured blocked providers
52
+ const blockedProviders = organization && privateOrganizations . includes ( organization ) ? [ ] :
53
+ Object . entries ( commitConfig . diff . block . providers ) ;
54
+
55
+ // Combine all matches (literals, paterns)
56
+ const combinedMatches = [
57
+ ...blockedLiterals . map ( literal => ( {
58
+ type : BLOCK_TYPE . LITERAL ,
59
+ match : new RegExp ( literal , 'gi' )
60
+ } ) ) ,
61
+ ...blockedPatterns . map ( pattern => ( {
62
+ type : BLOCK_TYPE . PATTERN ,
63
+ match : new RegExp ( pattern , 'gi' )
64
+ } ) ) ,
65
+ ...blockedProviders . map ( ( [ key , value ] ) => ( {
66
+ type : key ,
67
+ match : new RegExp ( value , 'gi' )
68
+ } ) ) ,
69
+ ] ;
70
+ return combinedMatches ;
71
+ }
72
+
73
+ const collectMatches = ( parsedDiff , combinedMatches ) => {
74
+ const allMatches = { } ;
75
+ parsedDiff . forEach ( file => {
76
+ const fileName = file . to || file . from ;
77
+ console . log ( "CHANGE" , file . chunks )
78
+
79
+ file . chunks . forEach ( chunk => {
80
+ chunk . changes . forEach ( change => {
81
+ if ( change . add ) {
82
+ // store line number
83
+ const lineNumber = change . ln ;
84
+ // Iterate through each match types - literal, patterns, providers
85
+ combinedMatches . forEach ( ( { type, match } ) => {
86
+ // using Match all to find all occurences of the pattern in the line
87
+ const matches = [ ...change . content . matchAll ( match ) ]
88
+
89
+ matches . forEach ( matchInstance => {
90
+ const matchLiteral = matchInstance [ 0 ] ;
91
+ const matchKey = `${ type } _${ matchLiteral } _${ fileName } ` ; // unique key
92
+
93
+
94
+ if ( ! allMatches [ matchKey ] ) {
95
+ // match entry
96
+ allMatches [ matchKey ] = {
97
+ type,
98
+ literal : matchLiteral ,
99
+ file : fileName ,
100
+ lines : [ ] ,
101
+ content : change . content . trim ( )
102
+ } ;
103
+ }
104
+
105
+ // apend line numbers to the list of lines
106
+ allMatches [ matchKey ] . lines . push ( lineNumber )
107
+ } )
108
+ } ) ;
109
+ }
110
+ } ) ;
111
+ } ) ;
112
+ } ) ;
113
+
114
+ // convert matches into a final result array, joining line numbers
115
+ const result = Object . values ( allMatches ) . map ( match => ( {
116
+ ...match ,
117
+ lines : match . lines . join ( ',' ) // join the line numbers into a comma-separated string
118
+ } ) )
119
+
120
+ console . log ( "RESULT" , result )
121
+ return result ;
122
+ }
123
+
124
+ const formatMatches = ( matches ) => {
125
+ return matches . map ( ( match , index ) => {
126
+ return `---------------------------------- #${ index + 1 } ${ match . type } ------------------------------
127
+ Policy Exception Type: ${ match . type }
128
+ DETECTED: ${ match . literal }
129
+ FILE(S) LOCATED: ${ match . file }
130
+ Line(s) of code: ${ match . lines } `
131
+ } ) ;
132
+ }
133
+
71
134
const exec = async ( req , action ) => {
72
135
const step = new Step ( 'scanDiff' ) ;
73
136
@@ -76,17 +139,26 @@ const exec = async (req, action) => {
76
139
77
140
const diff = steps . find ( ( s ) => s . stepName === 'diff' ) ?. content ;
78
141
79
- const legalDiff = isDiffLegal ( diff , action . project ) ;
142
+ console . log ( diff )
143
+ const diffViolations = getDiffViolations ( diff , action . project ) ;
144
+
145
+ if ( diffViolations ) {
146
+ const formattedMatches = Array . isArray ( diffViolations ) ? formatMatches ( diffViolations ) . join ( '\n\n' ) : diffViolations ;
147
+ const errorMsg = [ ] ;
148
+ errorMsg . push ( `\n\n\n\nYour push has been blocked.\n` ) ;
149
+ errorMsg . push ( `Please ensure your code does not contain sensitive information or URLs.\n\n` ) ;
150
+ errorMsg . push ( formattedMatches )
151
+ errorMsg . push ( '\n' )
80
152
81
- if ( ! legalDiff ) {
82
153
console . log ( `The following diff is illegal: ${ commitFrom } :${ commitTo } ` ) ;
83
154
84
155
step . error = true ;
85
156
step . log ( `The following diff is illegal: ${ commitFrom } :${ commitTo } ` ) ;
86
157
step . setError (
87
- '\n\n\n\nYour push has been blocked.\nPlease ensure your code does not contain sensitive information or URLs.\n\n\n' ,
158
+ errorMsg . join ( '\n' )
88
159
) ;
89
160
161
+
90
162
action . addStep ( step ) ;
91
163
return action ;
92
164
}
0 commit comments