@@ -378,6 +378,10 @@ module API {
378378 *
379379 * Anything in the global scope is considered to be an entry point, but
380380 * additional entry points may be added by extending this class.
381+ *
382+ * By default, instances of this class will have a single edge from the root
383+ * of the API graph. If you wish to construct more complex paths to an entry
384+ * point, override the `edge` predicate.
381385 */
382386 abstract class EntryPoint extends string {
383387 bindingset [ this ]
@@ -400,6 +404,41 @@ module API {
400404
401405 /** Gets an API-node for this entry point. */
402406 API:: Node getANode ( ) { result = root ( ) .getASuccessor ( Label:: entryPoint ( this ) ) }
407+
408+ /**
409+ * Holds if there is an edge from `pred` to this entry point, with label
410+ * `lbl`. Override this predicate to define new paths to this entry point.
411+ *
412+ * For example, to define an entry point for `ActiveStorage::Attachment` we
413+ * can use an intermediate entry point for `ActiveStorage`:
414+ *
415+ * ```ql
416+ * class ActiveStorage extends EntryPoint {
417+ * ActiveStorage() { this = "ActiveStorage" }
418+ *
419+ * override predicate edge(Node pred, Label::ApiLabel lbl) {
420+ * pred = root() and lbl = Label::member("ActiveStorage")
421+ * }
422+ * }
423+ *
424+ * class Attachment extends EntryPoint {
425+ * Attachment() { this = "ActiveStorage::Attachment" }
426+ *
427+ * override predicate edge(Node pred, Label::ApiLabel lbl) {
428+ * pred = getTopLevelMember("ActiveStorage") and
429+ * lbl = Label::member("Attachment")
430+ * }
431+ *
432+ * override DataFlow::LocalSourceNode getAUse() { result = customAttachmentPredicate() }
433+ * }
434+ * ```
435+ *
436+ * This means that
437+ * `getTopLevelMember("ActiveStorage").getMember("Attachment")` will return
438+ * results from `customAttachmentPredicate()`, even if there are no
439+ * references to `ActiveStorage` or `Attachment` in the codebase.
440+ */
441+ predicate edge ( API:: Node pred , Label:: ApiLabel lbl ) { none ( ) }
403442 }
404443
405444 // Ensure all entry points are imported from ApiGraphs.qll
@@ -718,6 +757,8 @@ module API {
718757 exists ( EntryPoint entry |
719758 pred = root ( ) and
720759 lbl = Label:: entryPoint ( entry )
760+ or
761+ entry .edge ( pred , lbl )
721762 |
722763 succ = MkDef ( entry .getASink ( ) )
723764 or
0 commit comments