diff --git a/modules/drivers/mongo/src/metabase/driver/mongo.clj b/modules/drivers/mongo/src/metabase/driver/mongo.clj
index 1edbb9609388e922421bb5557e5d056f682683d6..4971081aec3ad0869d41dc0d8f6ad7c13526b711 100644
--- a/modules/drivers/mongo/src/metabase/driver/mongo.clj
+++ b/modules/drivers/mongo/src/metabase/driver/mongo.clj
@@ -264,8 +264,9 @@
                               :inner-join                      true
                               :left-join                       true
                               :nested-fields                   true
-                              :nested-queries                  true
+                              :native-parameter-card-reference false
                               :native-parameters               true
+                              :nested-queries                  true
                               :set-timezone                    true
                               :standard-deviation-aggregations true
                               :test/jvm-timezone-setting       false
diff --git a/src/metabase/driver.clj b/src/metabase/driver.clj
index b6fc21b214f7abc7c5d4a37ffb5c77e7d383a81e..b463d203edb95c77c3e8757299d31e3169469668 100644
--- a/src/metabase/driver.clj
+++ b/src/metabase/driver.clj
@@ -563,6 +563,16 @@
     ;; subselects in SQL queries.
     :nested-queries
 
+    ;; Does this driver support native template tag parameters of type `:card`, e.g. in a native query like
+    ;;
+    ;;    SELECT * FROM {{card}}
+    ;;
+    ;; do we support substituting `{{card}}` with another compiled (nested) query?
+    ;;
+    ;; By default, this is true for drivers that support `:native-parameters` and `:nested-queries`, but drivers can opt
+    ;; out if they do not support Card ID template tag parameters.
+    :native-parameter-card-reference
+
     ;; Does the driver support persisting models
     :persist-models
     ;; Is persisting enabled?
@@ -712,6 +722,13 @@
                               :upload-with-auto-pk                    true}]
   (defmethod database-supports? [::driver feature] [_driver _feature _db] supported?))
 
+;;; By default a driver supports `:native-parameter-card-reference` if it supports `:native-parameters` AND
+;;; `:nested-queries`.
+(defmethod database-supports? [::driver :native-parameter-card-reference]
+  [driver _feature database]
+  (and (database-supports? driver :native-parameters database)
+       (database-supports? driver :nested-queries database)))
+
 (defmulti ^String escape-alias
   "Escape a `column-or-table-alias` string in a way that makes it valid for your database. This method is used for
   existing columns; aggregate functions and other expressions; joined tables; and joined subqueries; be sure to return
diff --git a/src/metabase/driver/common/parameters/values.clj b/src/metabase/driver/common/parameters/values.clj
index f48338a3edfc593ab3bf2af0b83a77f3c83015f5..5db7278f3b503906a0d68c90af2068b6b5091526 100644
--- a/src/metabase/driver/common/parameters/values.clj
+++ b/src/metabase/driver/common/parameters/values.clj
@@ -404,3 +404,13 @@
                        :tags   tags
                        :params params}
                       e)))))
+
+(mu/defn referenced-card-ids :- [:set ::lib.schema.id/card]
+  "Return a set of all Card IDs referenced in the parameters in `params-map`. This should be added to the (inner) query
+  under the `:metabase.models.query.permissions/referenced-card-ids` key when doing parameter expansion."
+  [params-map :- [:map-of ::lib.schema.common/non-blank-string ParsedParamValue]]
+  (into #{}
+        (keep (fn [param]
+                (when (params/ReferencedCardQuery? param)
+                  (:card-id param))))
+        (vals params-map)))
diff --git a/src/metabase/driver/sql.clj b/src/metabase/driver/sql.clj
index 2172e10323ecad7e303cb0881a2edf2c259d69b8..09e669ddd7dcc54539ae5d12295d4c452c869c7f 100644
--- a/src/metabase/driver/sql.clj
+++ b/src/metabase/driver/sql.clj
@@ -1,6 +1,7 @@
 (ns metabase.driver.sql
   "Shared code for all drivers that use SQL under the hood."
   (:require
+   [clojure.set :as set]
    [metabase.driver :as driver]
    [metabase.driver.common.parameters.parse :as params.parse]
    [metabase.driver.common.parameters.values :as params.values]
@@ -55,12 +56,16 @@
 
 (mu/defmethod driver/substitute-native-parameters :sql
   [_driver {:keys [query] :as inner-query} :- [:and [:map-of :keyword :any] [:map {:query ::lib.schema.common/non-blank-string}]]]
-  (let [[query params] (-> query
-                           params.parse/parse
-                           (sql.params.substitute/substitute (params.values/query->params-map inner-query)))]
-    (assoc inner-query
-           :query query
-           :params params)))
+  (let [params-map          (params.values/query->params-map inner-query)
+        referenced-card-ids (params.values/referenced-card-ids params-map)
+        [query params]      (-> query
+                                params.parse/parse
+                                (sql.params.substitute/substitute params-map))]
+    (cond-> (assoc inner-query
+                   :query  query
+                   :params params)
+      (seq referenced-card-ids)
+      (update :metabase.models.query.permissions/referenced-card-ids set/union referenced-card-ids))))
 
 
 ;;; +----------------------------------------------------------------------------------------------------------------+
diff --git a/src/metabase/lib/schema.cljc b/src/metabase/lib/schema.cljc
index 5730ff8332f237e467a7bb23c02cabfafbc9b51e..f07bc9e65b7cb3237467a3ae36ae409967895685 100644
--- a/src/metabase/lib/schema.cljc
+++ b/src/metabase/lib/schema.cljc
@@ -60,6 +60,10 @@
     ;; optional template tag declarations. Template tags are things like `{{x}}` in the query (the value of the
     ;; `:native` key), but their definition lives under this key.
     [:template-tags {:optional true} [:ref ::template-tag/template-tag-map]]
+    ;; optional, set of Card IDs referenced by this query in `:card` template tags like `{{card}}`. This is added
+    ;; automatically during parameter expansion. To run a native query you must have native query permissions as well
+    ;; as permissions for any Cards' parent Collections used in `:card` template tag parameters.
+    [:metabase.models.query.permissions/referenced-card-ids {:optional true} [:maybe [:set ::id/card]]]
     ;;
     ;; TODO -- parameters??
     ]
diff --git a/src/metabase/models/query/permissions.clj b/src/metabase/models/query/permissions.clj
index 25a199232944aebc563036e76f298c6e17636b1e..f9ac93904dd9b15d6599988b4cec206103b93949 100644
--- a/src/metabase/models/query/permissions.clj
+++ b/src/metabase/models/query/permissions.clj
@@ -4,6 +4,7 @@
   as a Card. Saved Cards are subject to the permissions of the Collection to which they belong."
   (:require
    [clojure.set :as set]
+   [clojure.walk :as walk]
    [metabase.api.common :as api]
    [metabase.legacy-mbql.normalize :as mbql.normalize]
    [metabase.lib.core :as lib]
@@ -45,17 +46,17 @@
 ;; Is calculating permissions for queries complicated? Some would say so. Refer to this handy flow chart to see how
 ;; things get calculated.
 ;;
-;;                                  perms-set
-;;                                      |
-;;                                      |
-;;                                      |
-;;               native query? <--------+---------> mbql query?
-;;                     ↓                                     ↓
-;;    {:perms/create-queries :query-builder-and-native}  legacy-mbql-required-perms
-;;                                                           |
-;;                                  no source card  <--------+------> has source card
-;;                                          ↓                            ↓
-;;                    {:perms/view-data {table-id :unrestricted}}  source-card-read-perms
+;;                         perms-set
+;;                             |
+;;                             |
+;;                             |
+;;      native query? <--------+---------> mbql query?
+;;            ↓                                     ↓
+;;    native-query-perms              legacy-mbql-required-perms
+;;                                                  |
+;;                         no source card  <--------+------> has source card
+;;                                 ↓                            ↓
+;;           {:perms/view-data {table-id :unrestricted}}  source-card-read-perms
 ;;
 
 (mu/defn query->source-table-ids :- [:set [:or [:= ::native] ::lib.schema.id/table]]
@@ -103,6 +104,30 @@
   (binding [api/*current-user-id* nil]
     ((requiring-resolve 'metabase.query-processor.preprocess/preprocess) query)))
 
+(defn- referenced-card-ids
+  "Return the union of all the `::referenced-card-ids` sets anywhere in the query."
+  [query]
+  (let [all-ids (atom #{})]
+    (walk/postwalk
+     (fn [form]
+       (when (map? form)
+         (when-let [ids (not-empty (::referenced-card-ids form))]
+           (swap! all-ids set/union ids)))
+       form)
+     query)
+    (not-empty @all-ids)))
+
+(defn- native-query-perms
+  [query]
+  (merge
+   {:perms/create-queries :query-builder-and-native
+    :perms/view-data      :unrestricted}
+   (when-let [card-ids (referenced-card-ids query)]
+     {:paths (into #{}
+                   (mapcat (fn [card-id]
+                             (mi/perms-objects-set (card-instance card-id) :read)))
+                   card-ids)})))
+
 (defn- legacy-mbql-required-perms
   [query {:keys [throw-exceptions? already-preprocessed?]}]
   (try
@@ -117,7 +142,7 @@
               table-ids           (filter integer? table-ids-or-native)
               native?             (.contains ^clojure.lang.PersistentVector table-ids-or-native ::native)]
           (merge
-           {:perms/view-data :unrestricted}
+           (native-query-perms query)
            (when (seq table-ids)
              {:perms/create-queries (zipmap table-ids (repeat :query-builder))})
            (when native?
@@ -151,8 +176,7 @@
     {}
     (let [query-type (lib/normalized-query-type query)]
       (case query-type
-        :native     {:perms/create-queries :query-builder-and-native
-                     :perms/view-data :unrestricted}
+        :native     (native-query-perms query)
         :query      (legacy-mbql-required-perms query perms-opts)
         :mbql/query (pmbql-required-perms query perms-opts)
         (throw (ex-info (tru "Invalid query type: {0}" query-type)
diff --git a/test/metabase/driver/common/parameters/values_test.clj b/test/metabase/driver/common/parameters/values_test.clj
index 5ea90b9c850dff7c99d753389b58f755d40b772a..76f6300e6256fc444e26e2ff09d802eb70917148 100644
--- a/test/metabase/driver/common/parameters/values_test.clj
+++ b/test/metabase/driver/common/parameters/values_test.clj
@@ -837,3 +837,25 @@
                                                         {:type   :date/month-year
                                                          :value  "2023-01"
                                                          :target [:dimension [:template-tag "createdAt"]]}]})))))))))
+
+(deftest ^:parallel referenced-card-ids-test
+  (mt/with-temp [:model/Card {card-1-id :id} {:collection_id nil
+                                              :dataset_query (mt/mbql-query venues {:limit 2})}
+                 :model/Card {card-2-id :id} {:collection_id nil
+                                              :dataset_query (mt/native-query
+                                                              {:query         "SELECT * FROM {{card}}"
+                                                               :template-tags {"card" {:name         "card"
+                                                                                       :display-name "card"
+                                                                                       :type         :card
+                                                                                       :card-id      card-1-id}}})}]
+    ;; even tho Card 2 references Card 1, we don't want to include it in the set of referenced Card IDs, since you
+    ;; should only need permissions for Card 2 to be able to run the query (see #15131)
+    (testing (format "Card 1 ID = %d, Card 2 ID = %d" card-1-id card-2-id)
+      (mt/with-metadata-provider (mt/id)
+        (is (=? #{card-2-id}
+                (params.values/referenced-card-ids (params.values/query->params-map
+                                                    {:query         "SELECT * FROM {{card}}"
+                                                     :template-tags {"card" {:name         "card"
+                                                                             :display-name "card"
+                                                                             :type         :card
+                                                                             :card-id      card-2-id}}}))))))))
diff --git a/test/metabase/models/query/permissions_test.clj b/test/metabase/models/query/permissions_test.clj
index 2695e891c48402a9646597242d571f9626762fa2..bd5b3aee4b5bcce0d3c2058fb3e39dc711c4417c 100644
--- a/test/metabase/models/query/permissions_test.clj
+++ b/test/metabase/models/query/permissions_test.clj
@@ -263,3 +263,42 @@
       (is (= {:perms/view-data :unrestricted
               :perms/create-queries :query-builder-and-native}
              (query-perms/required-perms query))))))
+
+(deftest ^:parallel native-query-referenced-card-permissions-test
+  (testing "Check permissions for native query card reference parameters (the `:metabase.models.query.permissions/referenced-card-ids` key(s))"
+    (mt/with-temp [:model/Collection {collection-1-id :id} {}
+                   :model/Collection {collection-2-id :id} {}
+                   :model/Card       {card-1-id :id}       {:collection_id collection-1-id}
+                   :model/Card       {card-2-id :id}       {:collection_id collection-2-id}]
+      (testing "native query"
+        (is (= {:perms/create-queries :query-builder-and-native
+                :perms/view-data      :unrestricted
+                :paths                #{(format "/collection/%d/read/" collection-1-id)
+                                        (format "/collection/%d/read/" collection-2-id)}}
+               (query-perms/required-perms
+                {:database                         (mt/id)
+                 :type                             :native
+                 :native                           "SELECT * FROM (SELECT * FROM whatever);"
+                 ::query-perms/referenced-card-ids #{card-1-id card-2-id}}))))
+      (testing "MBQL query with native source queries"
+        (let [native-query {:database (mt/id)
+                            :type     :query
+                            :query    {:source-query {:native                           "SELECT * FROM (SELECT * FROM whatever);"
+                                                      ::query-perms/referenced-card-ids #{card-1-id}}
+                                       :joins        [{:alias        "J"
+                                                       :source-query {:native                           "SELECT * FROM (SELECT * FROM whatever);"
+                                                                      ::query-perms/referenced-card-ids #{card-2-id}}
+                                                       :condition    [:= true false]}]}}]
+          (is (= {:perms/create-queries :query-builder-and-native
+                  :perms/view-data      :unrestricted
+                  :paths                #{(format "/collection/%d/read/" collection-1-id)
+                                          (format "/collection/%d/read/" collection-2-id)}}
+                 (query-perms/required-perms native-query)))
+          (testing "pMBQL query"
+            (is (= {:perms/create-queries :query-builder-and-native
+                    :perms/view-data      :unrestricted
+                    :paths                #{(format "/collection/%d/read/" collection-1-id)
+                                            (format "/collection/%d/read/" collection-2-id)}}
+                   (query-perms/required-perms
+                    (lib/query (lib.metadata.jvm/application-database-metadata-provider (mt/id))
+                               (lib/->pMBQL native-query)))))))))))
diff --git a/test/metabase/query_processor/middleware/parameters/native_test.clj b/test/metabase/query_processor/middleware/parameters/native_test.clj
index 455896520cf7c2d59a8f5a324d402f596e190aef..11f243ae16335d9254a068ef57e724a6e86f11d1 100644
--- a/test/metabase/query_processor/middleware/parameters/native_test.clj
+++ b/test/metabase/query_processor/middleware/parameters/native_test.clj
@@ -1,6 +1,7 @@
 (ns metabase.query-processor.middleware.parameters.native-test
   (:require
    [clojure.test :refer :all]
+   [metabase.driver :as driver]
    [metabase.models.card :refer [Card]]
    [metabase.query-processor.middleware.parameters.native :as qp.native]
    [metabase.test :as mt]
@@ -31,3 +32,27 @@
                            [:native ms/NonBlankString]
                            [:params [:= ["G%"]]]]
                           (qp.native/expand-inner query))))))))))
+
+(deftest ^:parallel native-query-with-card-template-tag-include-referenced-card-ids-test
+  (mt/test-drivers (mt/normal-drivers-with-feature :native-parameters :nested-queries :native-parameter-card-reference)
+    (testing "Expanding a Card template tag should add the card ID(s) to `:metabase.models.query.permissions/referenced-card-ids`"
+      (mt/with-temp [:model/Card {card-1-id :id} {:collection_id nil
+                                                  :dataset_query (mt/mbql-query venues {:limit 2})}
+                     :model/Card {card-2-id :id} {:collection_id nil
+                                                  :dataset_query (mt/native-query
+                                                                   {:query         (mt/native-query-with-card-template-tag driver/*driver* "card")
+                                                                    :template-tags {"card" {:name         "card"
+                                                                                            :display-name "card"
+                                                                                            :type         :card
+                                                                                            :card-id      card-1-id}}})}]
+        (testing (format "Card 1 ID = %d, Card 2 ID = %d" card-1-id card-2-id)
+          ;; this SHOULD NOT include `card-1-id`, because Card 1 is only referenced indirectly; if you have permissions
+          ;; to run Card 2 that should be sufficient to run it even if it references Card 1 (see #15131)
+          (mt/with-metadata-provider (mt/id)
+            (is (=? {:metabase.models.query.permissions/referenced-card-ids #{Integer/MAX_VALUE card-2-id}}
+                    (qp.native/expand-inner {:query         (mt/native-query-with-card-template-tag driver/*driver* "card")
+                                             :template-tags {"card" {:name         "card"
+                                                                     :display-name "card"
+                                                                     :type         :card
+                                                                     :card-id      card-2-id}}
+                                             :metabase.models.query.permissions/referenced-card-ids #{Integer/MAX_VALUE}})))))))))
diff --git a/test/metabase/query_processor/middleware/parameters_test.clj b/test/metabase/query_processor/middleware/parameters_test.clj
index 5045a1f0775d296f33e17d55a6de6335590a339d..7dff651202edd6ff2d0fbabbd205b4be37f9e140 100644
--- a/test/metabase/query_processor/middleware/parameters_test.clj
+++ b/test/metabase/query_processor/middleware/parameters_test.clj
@@ -226,32 +226,32 @@
 (deftest ^:parallel expand-multiple-referenced-cards-in-template-tags
   (testing "multiple sub-queries, referenced in template tags, are correctly substituted"
     (qp.store/with-metadata-provider mock-native-query-cards-metadata-provider
-      (is (= (native-query
-              {:query "SELECT COUNT(*) FROM (SELECT 1) AS c1, (SELECT 2) AS c2", :params []})
-             (substitute-params
-              (native-query
-               {:query         (str "SELECT COUNT(*) FROM {{#" 1 "}} AS c1, {{#" 2 "}} AS c2")
-                :template-tags (card-template-tags [1 2])})))))))
+      (is (=? (native-query
+               {:query "SELECT COUNT(*) FROM (SELECT 1) AS c1, (SELECT 2) AS c2", :params []})
+              (substitute-params
+               (native-query
+                {:query         (str "SELECT COUNT(*) FROM {{#" 1 "}} AS c1, {{#" 2 "}} AS c2")
+                 :template-tags (card-template-tags [1 2])})))))))
 
 (deftest ^:parallel expand-multiple-referenced-cards-in-template-tags-2
   (testing "multiple CTE queries, referenced in template tags, are correctly substituted"
     (qp.store/with-metadata-provider mock-native-query-cards-metadata-provider
-      (is (= (native-query
-              {:query "WITH c1 AS (SELECT 1), c2 AS (SELECT 2) SELECT COUNT(*) FROM c1, c2", :params []})
-             (substitute-params
-              (native-query
-               {:query         "WITH c1 AS {{#1}}, c2 AS {{#2}} SELECT COUNT(*) FROM c1, c2"
-                :template-tags (card-template-tags [1 2])})))))))
+      (is (=? (native-query
+               {:query "WITH c1 AS (SELECT 1), c2 AS (SELECT 2) SELECT COUNT(*) FROM c1, c2", :params []})
+              (substitute-params
+               (native-query
+                {:query         "WITH c1 AS {{#1}}, c2 AS {{#2}} SELECT COUNT(*) FROM c1, c2"
+                 :template-tags (card-template-tags [1 2])})))))))
 
 (deftest ^:parallel expand-multiple-referenced-cards-in-template-tags-3
   (testing "recursive native queries, referenced in template tags, are correctly substituted"
     (qp.store/with-metadata-provider mock-native-query-cards-metadata-provider
-      (is (= (native-query
-              {:query "SELECT COUNT(*) FROM (SELECT * FROM (SELECT 1) AS c1) AS c2", :params []})
-             (substitute-params
-              (native-query
-               {:query         "SELECT COUNT(*) FROM {{#3}} AS c2"
-                :template-tags (card-template-tags [3])})))))))
+      (is (=? (native-query
+               {:query "SELECT COUNT(*) FROM (SELECT * FROM (SELECT 1) AS c1) AS c2", :params []})
+              (substitute-params
+               (native-query
+                {:query         "SELECT COUNT(*) FROM {{#3}} AS c2"
+                 :template-tags (card-template-tags [3])})))))))
 
 (deftest ^:parallel expand-multiple-referenced-cards-in-template-tags-4
   (testing "recursive native/MBQL queries, referenced in template tags, are correctly substituted"
@@ -269,12 +269,12 @@
                                  "\"PUBLIC\".\"VENUES\".\"LONGITUDE\" AS \"LONGITUDE\", "
                                  "\"PUBLIC\".\"VENUES\".\"PRICE\" AS \"PRICE\" "
                                  "FROM \"PUBLIC\".\"VENUES\"")]
-        (is (= (native-query
-                {:query (str "SELECT COUNT(*) FROM (SELECT * FROM (" card-1-subquery ") AS c1) AS c2") :params []})
-               (substitute-params
-                (native-query
-                 {:query         "SELECT COUNT(*) FROM {{#2}} AS c2"
-                  :template-tags (card-template-tags [2])}))))))))
+        (is (=? (native-query
+                 {:query (str "SELECT COUNT(*) FROM (SELECT * FROM (" card-1-subquery ") AS c1) AS c2") :params []})
+                (substitute-params
+                 (native-query
+                  {:query         "SELECT COUNT(*) FROM {{#2}} AS c2"
+                   :template-tags (card-template-tags [2])}))))))))
 
 (deftest ^:parallel referencing-cards-with-parameters-test
   (testing "referencing card with parameter and default value substitutes correctly"
@@ -289,12 +289,12 @@
                                                           :type         :number
                                                           :default      "1"
                                                           :required     true}}})])
-      (is (= (native-query
-              {:query "SELECT * FROM (SELECT 1) AS x", :params []})
-             (substitute-params
-              (native-query
-               {:query         "SELECT * FROM {{#1}} AS x"
-                :template-tags (card-template-tags [1])})))))))
+      (is (=? (native-query
+               {:query "SELECT * FROM (SELECT 1) AS x", :params []})
+              (substitute-params
+               (native-query
+                {:query         "SELECT * FROM {{#1}} AS x"
+                 :template-tags (card-template-tags [1])})))))))
 
 (deftest ^:parallel referencing-cards-with-parameters-test-2
   (testing "referencing card with parameter and NO default value, fails substitution"
@@ -348,12 +348,12 @@
                  {:query "SELECT name, price FROM venues WHERE price > 2", :params nil})
                (substitute-params (:dataset_query card)))))
       (testing "multiple snippets are expanded from saved sub-query"
-        (is (= (mt/native-query
-                 {:query "SELECT * FROM (SELECT name, price FROM venues WHERE price > 2) AS x", :params []})
-               (substitute-params
-                (mt/native-query
-                  {:query         (str "SELECT * FROM {{#" (:id card) "}} AS x")
-                   :template-tags (card-template-tags [(:id card)])}))))))))
+        (is (=? (mt/native-query
+                  {:query "SELECT * FROM (SELECT name, price FROM venues WHERE price > 2) AS x", :params []})
+                (substitute-params
+                 (mt/native-query
+                   {:query         (str "SELECT * FROM {{#" (:id card) "}} AS x")
+                    :template-tags (card-template-tags [(:id card)])}))))))))
 
 (deftest ^:parallel include-card-parameters-test
   (testing "Expanding a Card reference should include its parameters (#12236)"
diff --git a/test/metabase/query_processor_test/parameters_test.clj b/test/metabase/query_processor_test/parameters_test.clj
index 7e902b735aa603b38a88c4dc72f11c91287ce953..7bff48e1d079b4170090114d95e25983ff079e47 100644
--- a/test/metabase/query_processor_test/parameters_test.clj
+++ b/test/metabase/query_processor_test/parameters_test.clj
@@ -4,11 +4,14 @@
   (:require
    [clojure.string :as str]
    [clojure.test :refer :all]
+   [clojure.walk :as walk]
    [java-time.api :as t]
    [medley.core :as m]
    [metabase.driver :as driver]
    [metabase.lib.native :as lib-native]
    [metabase.models :refer [Card]]
+   [metabase.models.permissions :as perms]
+   [metabase.models.permissions-group :as perms-group]
    [metabase.query-processor :as qp]
    [metabase.query-processor.compile :as qp.compile]
    [metabase.test :as mt]
@@ -512,3 +515,80 @@
                              :target [:variable [:template-tag "n"]]
                              :slug "n"
                              :value "30"}]}))))))
+
+(deftest sql-permissions-but-no-card-permissions-template-tag-test
+  (testing "If we have full SQL perms for a DW but no Card perms we shouldn't be able to include it with a ref or template tag"
+    (mt/test-drivers (mt/normal-drivers-with-feature :native-parameters :nested-queries :native-parameter-card-reference)
+      (mt/with-non-admin-groups-no-root-collection-perms
+        (mt/with-temp [:model/Collection {collection-1-id :id} {}
+                       :model/Collection {collection-2-id :id} {}
+
+                       :model/Card
+                       {card-1-id :id}
+                       {:collection_id collection-1-id
+                        :dataset_query (mt/mbql-query venues {:fields   [$id $name]
+                                                              :order-by [[:asc $id]]
+                                                              :limit    2})}
+
+                       :model/Card
+                       {card-2-id :id, :as card-2}
+                       {:collection_id collection-2-id
+                        :dataset_query (mt/native-query
+                                         {:query         (mt/native-query-with-card-template-tag driver/*driver* "card")
+                                          :template-tags {"card" {:name         "card"
+                                                                  :display-name "card"
+                                                                  :type         :card
+                                                                  :card-id      card-1-id}}})}]
+          (testing (format "\nCollection 1 ID = %d, Card 1 ID = %d; Collection 2 ID = %d, Card 2 ID = %d"
+                           collection-1-id card-1-id collection-2-id card-2-id)
+            (mt/with-test-user :rasta
+              (testing "Sanity check: shouldn't be able to run Card as MBQL query"
+                (is (thrown-with-msg?
+                     clojure.lang.ExceptionInfo
+                     #"You do not have permissions to view Card \d+"
+                     (qp/process-query {:database (mt/id), :type :query, :query {:source-table (format "card__%d" card-2-id)}}))))
+              (testing "Sanity check: SHOULD be able to run a native query"
+                (testing (str "COMPILED = \n" (u/pprint-to-str (qp.compile/compile (:dataset_query card-2))))
+                  (is (= [[1 "Red Medicine"]
+                          [2 "Stout Burgers & Beers"]]
+                         (mt/formatted-rows
+                          [int str]
+                          (qp/process-query {:database (mt/id)
+                                             :type     :native
+                                             :native   (dissoc (qp.compile/compile (:dataset_query card-2))
+                                                               :metabase.models.query.permissions/referenced-card-ids)}))))))
+              (let [query (mt/native-query
+                            {:query         (mt/native-query-with-card-template-tag driver/*driver* "card")
+                             :template-tags {"card" {:name         "card"
+                                                     :display-name "card"
+                                                     :type         :card
+                                                     :card-id      card-2-id}}})]
+                (testing "SHOULD NOT be able to run native query with Card ID template tag"
+                  (is (thrown-with-msg?
+                       clojure.lang.ExceptionInfo
+                       #"\QYou do not have permissions to run this query.\E"
+                       (qp/process-query query))))
+                (testing "Exception should NOT include the compiled native query"
+                  (try
+                    (qp/process-query query)
+                    (is (not ::here?)
+                        "Should never get here, query should throw an Exception")
+                    (catch Throwable e
+                      (doseq [data (keep ex-data (u/full-exception-chain e))]
+                        (walk/postwalk
+                         (fn [form]
+                           (when (string? form)
+                             (is (not (re-find #"SELECT" form))))
+                           form)
+                         data)))))
+                (testing (str "If we have permissions for Card 2's Collection (but not Card 1's) we should be able to"
+                              " run a native query referencing Card 2, even tho it references Card 1 (#15131)")
+                  (perms/grant-collection-read-permissions! (perms-group/all-users) collection-2-id)
+                  ;; need to call [[mt/with-test-user]] again so [[metabase.api.common/*current-user-permissions-set*]]
+                  ;; gets rebound with the updated permissions. This will be fixed in #45001
+                  (mt/with-test-user :rasta
+                    (is (= [[1 "Red Medicine"]
+                            [2 "Stout Burgers & Beers"]]
+                           (mt/formatted-rows
+                            [int str]
+                            (qp/process-query query))))))))))))))
diff --git a/test/metabase/test.clj b/test/metabase/test.clj
index e5b04bfa473ab418f6737a4a77a95c8f76cabc40..3fee3e59ead56896b595ead4c0e9ddec34e5c4a3 100644
--- a/test/metabase/test.clj
+++ b/test/metabase/test.clj
@@ -311,6 +311,7 @@
   get-dataset-definition
   has-test-extensions?
   metabase-instance
+  native-query-with-card-template-tag
   sorts-nil-first?
   supports-time-type?
   supports-timestamptz-type?]
diff --git a/test/metabase/test/data/interface.clj b/test/metabase/test/data/interface.clj
index 9c7537a68ae691bb208678d4df940f46fbeec2e6..54d60b84b424c489e1a6288f778fc6fd869cf4f0 100644
--- a/test/metabase/test/data/interface.clj
+++ b/test/metabase/test/data/interface.clj
@@ -793,3 +793,13 @@
   ;; Following cyclic dependency by that requiring resolve.
   ((requiring-resolve 'metabase.test.data.impl/resolve-dataset-definition)
    'metabase.test.data.dataset-definitions 'test-data))
+
+(defmulti native-query-with-card-template-tag
+  "For drivers that support `:native-parameter-card-reference`:
+
+  Return a native `:query` (just the SQL string or equivalent with a `:card` template tag e.g.
+
+    \"SELECT * FROM {{%s}}\""
+  {:arglists '([driver card-template-tag-name])}
+  dispatch-on-driver-with-test-extensions
+  :hierarchy #'driver/hierarchy)
diff --git a/test/metabase/test/data/sql.clj b/test/metabase/test/data/sql.clj
index 0b050d78528b00e017dc0eafd995de76c543b392..569ec3baa783713ca0f633a57ac9f97f6751eb32 100644
--- a/test/metabase/test/data/sql.clj
+++ b/test/metabase/test/data/sql.clj
@@ -9,7 +9,9 @@
    [metabase.query-processor.compile :as qp.compile]
    [metabase.test.data :as data]
    [metabase.test.data.interface :as tx]
-   [metabase.util.log :as log]))
+   [metabase.util :as u]
+   [metabase.util.log :as log]
+   [metabase.util.random :as u.random]))
 
 (comment metabase.driver.sql/keep-me)
 
@@ -344,3 +346,8 @@
   :hierarchy #'driver/hierarchy)
 
 (defmethod session-schema :sql/test-extensions [_] nil)
+
+(defmethod tx/native-query-with-card-template-tag :sql
+  [_driver card-template-tag-name]
+  (let [source-table-name (u/lower-case-en (u.random/random-name))]
+    (format "SELECT * FROM {{%s}} %s" card-template-tag-name source-table-name)))