@@ -57,14 +57,26 @@ module ActiveStorage {
57
57
// "activestorage;Blob;activestorage;;Member[ActiveStorage].Member[Blob].Method[compose].Argument[0].Element[any]",
58
58
// ActiveStorage::Blob.find_signed(!) : Blob
59
59
"activestorage;Blob;activestorage;;Member[ActiveStorage].Member[Blob].Method[find_signed,find_signed!].ReturnValue" ,
60
- // ActiveStorage::Attachment#blob : Blob
61
- "activestorage;Blob;activestorage;;Member[ActiveStorage].Member[Attachment].Instance.Method[blob].ReturnValue" ,
62
- // ActiveStorage::Attachment delegates method calls to its associated Blob
63
- "activestorage;Blob;activestorage;;Member[ActiveStorage].Member[Attachment].Instance" ,
64
60
]
65
61
}
66
62
}
67
63
64
+ private class BlobInstance extends DataFlow:: Node {
65
+ BlobInstance ( ) {
66
+ this = ModelOutput:: getATypeNode ( "activestorage" , "Blob" ) .getAValueReachableFromSource ( )
67
+ or
68
+ // ActiveStorage::Attachment#blob : Blob
69
+ exists ( DataFlow:: CallNode call |
70
+ call = this and
71
+ call .getReceiver ( ) instanceof AttachmentInstance and
72
+ call .getMethodName ( ) = "blob"
73
+ )
74
+ or
75
+ // ActiveStorage::Attachment delegates method calls to its associated Blob
76
+ this instanceof AttachmentInstance
77
+ }
78
+ }
79
+
68
80
/**
69
81
* Method calls on `ActiveStorage::Blob` that send HTTP requests.
70
82
*/
@@ -78,11 +90,16 @@ module ActiveStorage {
78
90
.getASubclass * ( )
79
91
.getAMethodCall ( [ "create_after_unfurling!" , "create_and_upload!" ] ) ,
80
92
// Instance methods
81
- ModelOutput:: getATypeNode ( "activestorage" , "Blob" )
82
- .getAMethodCall ( [
83
- "upload" , "upload_without_unfurling" , "download" , "download_chunk" , "delete" ,
84
- "purge"
85
- ] )
93
+ any ( BlobInstance i , DataFlow:: CallNode c |
94
+ i .( DataFlow:: LocalSourceNode ) .flowsTo ( c .getReceiver ( ) ) and
95
+ c .getMethodName ( ) =
96
+ [
97
+ "upload" , "upload_without_unfurling" , "download" , "download_chunk" , "delete" ,
98
+ "purge"
99
+ ]
100
+ |
101
+ c
102
+ )
86
103
] .asExpr ( ) .getExpr ( )
87
104
}
88
105
@@ -110,7 +127,7 @@ module ActiveStorage {
110
127
}
111
128
112
129
/**
113
- * An ActiveStorage attachment, instantiated via an association with an
130
+ * An ActiveStorage attachment, instantiated directly or via an association with an
114
131
* ActiveRecord model.
115
132
*
116
133
* ```rb
@@ -120,18 +137,30 @@ module ActiveStorage {
120
137
*
121
138
* user = User.find(id)
122
139
* user.avatar
140
+ * ActiveStorage::Attachment.new
123
141
* ```
124
142
*/
125
- private class AttachmentInstance extends DataFlow:: CallNode {
143
+ class AttachmentInstance extends DataFlow:: Node {
126
144
AttachmentInstance ( ) {
127
- exists ( Association assoc , string model | model = assoc .getTargetModelName ( ) |
128
- this .getReceiver ( ) .( ActiveRecordInstance ) .getClass ( ) = assoc .getSourceClass ( ) and
145
+ this =
146
+ API:: getTopLevelMember ( "ActiveStorage" )
147
+ .getMember ( "Attachment" )
148
+ .getInstance ( )
149
+ .getAValueReachableFromSource ( )
150
+ or
151
+ exists ( Association assoc , string model , DataFlow:: CallNode call |
152
+ model = assoc .getTargetModelName ( )
153
+ |
154
+ call = this and
155
+ call .getReceiver ( ) .( ActiveRecordInstance ) .getClass ( ) = assoc .getSourceClass ( ) and
129
156
(
130
- assoc .isSingular ( ) and this .getMethodName ( ) = model
157
+ assoc .isSingular ( ) and call .getMethodName ( ) = model
131
158
or
132
- assoc .isCollection ( ) and this .getMethodName ( ) = model
159
+ assoc .isCollection ( ) and call .getMethodName ( ) = model
133
160
)
134
161
)
162
+ or
163
+ any ( AttachmentInstance i ) .( DataFlow:: LocalSourceNode ) .flowsTo ( this )
135
164
}
136
165
}
137
166
@@ -141,35 +170,41 @@ module ActiveStorage {
141
170
*/
142
171
private class ImageProcessingCall extends DataFlow:: CallNode , SystemCommandExecution:: Range {
143
172
ImageProcessingCall ( ) {
144
- this =
145
- ModelOutput :: getATypeNode ( "activestorage ", "Blob" )
146
- . getAMethodCall ( [ "variant" , "preview" , "representation" ] ) or
173
+ this . getReceiver ( ) instanceof BlobInstance and
174
+ this . getMethodName ( ) = [ "variant ", "preview" , "representation" ]
175
+ or
147
176
this =
148
177
API:: getTopLevelMember ( "ActiveStorage" )
149
178
.getMember ( "Attachment" )
150
179
.getInstance ( )
151
- .getAMethodCall ( [ "variant" , "preview" , "representation" ] ) or
180
+ .getAMethodCall ( [ "variant" , "preview" , "representation" ] )
181
+ or
152
182
this =
153
183
API:: getTopLevelMember ( "ActiveStorage" )
154
184
.getMember ( "Variation" )
155
- .getAMethodCall ( [ "new" , "wrap" , "encode" ] ) or
185
+ .getAMethodCall ( [ "new" , "wrap" , "encode" ] )
186
+ or
156
187
this =
157
188
API:: getTopLevelMember ( "ActiveStorage" )
158
189
.getMember ( "Variation" )
159
190
.getInstance ( )
160
- .getAMethodCall ( "transformations=" ) or
191
+ .getAMethodCall ( "transformations=" )
192
+ or
161
193
this =
162
194
API:: getTopLevelMember ( "ActiveStorage" )
163
195
.getMember ( "Transformers" )
164
196
.getMember ( "ImageProcessingTransformer" )
165
- .getAMethodCall ( "new" ) or
197
+ .getAMethodCall ( "new" )
198
+ or
166
199
this =
167
200
API:: getTopLevelMember ( "ActiveStorage" )
168
201
.getMember ( [ "Preview" , "VariantWithRecord" ] )
169
- .getAMethodCall ( "new" ) or
202
+ .getAMethodCall ( "new" )
203
+ or
170
204
// `ActiveStorage.paths` is a global hash whose values are passed to
171
205
// a `system` call.
172
- this = API:: getTopLevelMember ( "ActiveStorage" ) .getAMethodCall ( "paths=" ) or
206
+ this = API:: getTopLevelMember ( "ActiveStorage" ) .getAMethodCall ( "paths=" )
207
+ or
173
208
// `ActiveStorage.video_preview_arguments` is passed to a `system` call.
174
209
this = API:: getTopLevelMember ( "ActiveStorage" ) .getAMethodCall ( "video_preview_arguments=" )
175
210
}
@@ -187,57 +222,4 @@ module ActiveStorage {
187
222
188
223
override DataFlow:: Node getCode ( ) { result = this .getArgument ( 0 ) }
189
224
}
190
-
191
- /**
192
- * Adds ActiveStorage instances to the API graph.
193
- * Source code may not mention `ActiveStorage` or `ActiveStorage::Attachment`,
194
- * so we add synthetic nodes for them.
195
- */
196
- private module ApiNodes {
197
- class ActiveStorage extends API:: EntryPoint {
198
- ActiveStorage ( ) { this = "ActiveStorage" }
199
-
200
- override predicate edge ( API:: Node pred , API:: Label:: ApiLabel lbl ) {
201
- pred = API:: root ( ) and lbl = API:: Label:: member ( "ActiveStorage" )
202
- }
203
- }
204
-
205
- class Attachment extends API:: EntryPoint {
206
- Attachment ( ) { this = "ActiveStorage::Attachment" }
207
-
208
- override predicate edge ( API:: Node pred , API:: Label:: ApiLabel lbl ) {
209
- pred = API:: getTopLevelMember ( "ActiveStorage" ) and
210
- lbl = API:: Label:: member ( "Attachment" )
211
- }
212
- }
213
-
214
- class AttachmentNew extends API:: EntryPoint {
215
- AttachmentNew ( ) { this = "ActiveStorage::Attachment.new" }
216
-
217
- override predicate edge ( API:: Node pred , API:: Label:: ApiLabel lbl ) {
218
- pred = API:: getTopLevelMember ( "ActiveStorage" ) .getMember ( "Attachment" ) and
219
- lbl = API:: Label:: method ( "new" )
220
- }
221
- }
222
-
223
- /**
224
- * An API entry point for instances of `ActiveStorage::Attachment`.
225
- * These arise from calls to methods generated by `has_one_attached` and
226
- * `has_many_attached` associations.
227
- */
228
- class AttachmentInstanceNode extends API:: EntryPoint {
229
- AttachmentInstanceNode ( ) { this = "ActiveStorage::Attachment.new.ReturnValue" }
230
-
231
- override predicate edge ( API:: Node pred , API:: Label:: ApiLabel lbl ) {
232
- pred = API:: getTopLevelMember ( "ActiveStorage" ) .getMember ( "Attachment" ) .getMethod ( "new" ) and
233
- lbl = API:: Label:: return ( )
234
- }
235
-
236
- override DataFlow:: LocalSourceNode getAUse ( ) { result = any ( AttachmentInstance i ) }
237
-
238
- override DataFlow:: CallNode getACall ( ) {
239
- any ( AttachmentInstance i ) .flowsTo ( result .getReceiver ( ) )
240
- }
241
- }
242
- }
243
225
}
0 commit comments