HyukjinKwon commented on code in PR #58614:
URL: https://github.com/apache/spark/pull/58614#discussion_r4004067690


##########
sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala:
##########
@@ -7016,6 +7016,22 @@ object SQLConf {
     .booleanConf
     .createWithDefault(true)
 
+  // Kept here with the other `spark.sql.avro.*` entries rather than in 
`StaticSQLConf`, though it
+  // is static: `buildStaticConf` registers the static key wherever it is 
declared.
+  val AVRO_SCHEMA_URL_ALLOWED_SCHEMES =
+    buildStaticConf("spark.sql.avro.schemaUrlAllowedSchemes")
+      .internal()
+      .doc("A comma-separated allowlist of URI schemes permitted for the 
'avroSchemaUrl' Avro " +
+        "option. Empty by default, which permits any scheme and preserves the 
previous behavior; " +
+        "when non-empty, an avroSchemaUrl whose scheme is not listed is 
rejected before it is " +
+        "opened. This is a static configuration fixed when the SparkSession is 
created and not " +
+        "modifiable at runtime, so it is an operator-level boundary that a 
session cannot relax.")

Review Comment:
   Fixed. Took your suggested framing: the `.doc` (d325505) now says the 
allowlist restricts the scheme an `avroSchemaUrl` may name, not which file 
system serves it -- that is decided by `fs.<scheme>.impl`, which a session can 
still set. Dropped the "a session cannot relax it" claim from both the doc and 
the PR description. Separately, per finding 6, I moved the conf into 
`StaticSQLConf` next to the other 4.3.0 static allowlists (cb6ac21); 
`AvroSchemaUrlAllowlistSuite`'s `CANNOT_MODIFY_STATIC_CONFIG` case confirms the 
static registration still holds.
   



##########
connector/avro/src/test/scala/org/apache/spark/sql/avro/AvroSuite.scala:
##########
@@ -1255,6 +1255,91 @@ abstract class AvroSuite
     assertExceptionMsg[FileNotFoundException](e, "File not_exists.avsc does 
not exist")
   }
 
+  // spark.sql.avro.schemaUrlAllowedSchemes is a static SQL config, so it 
cannot be set with
+  // withSQLConf; these drive AvroOptions directly under a SQLConf provided 
via withExistingConf.
+  // testFile returns a "file:" URL, so its scheme is an explicit "file"; the 
scheme-less path that
+  // resolves against the default file system is covered by its own test below.
+  test("SPARK-59329: avroSchemaUrl scheme allowlist permits an allowed 
scheme") {
+    val avroSchemaUrl = testFile("test_sub.avsc")
+    val hadoopConf = spark.sessionState.newHadoopConf()
+    val conf = new SQLConf()
+    conf.setConf(SQLConf.AVRO_SCHEMA_URL_ALLOWED_SCHEMES, Seq("file"))
+    SQLConf.withExistingConf(conf) {
+      val options = new AvroOptions(Map("avroSchemaUrl" -> avroSchemaUrl), 
hadoopConf)
+      assert(options.schema.isDefined)
+    }
+  }
+
+  test("SPARK-59329: avroSchemaUrl allowlist rejects a disallowed scheme " +
+    "before opening the file system") {
+    // An explicit non-"file" scheme is rejected by the allowlist check, which 
runs before the
+    // file system for the URL is instantiated -- so this surfaces the clean 
allowlist error
+    // rather than a lower-level failure from trying to load the s3a file 
system. The URL uses an
+    // upper-case "S3A" scheme so the lower-case "s3a" in the message pins the 
scheme-side case
+    // folding: dropping the fold on the scheme leaves no lower-case "s3a" in 
the message.
+    val hadoopConf = spark.sessionState.newHadoopConf()
+    val conf = new SQLConf()
+    conf.setConf(SQLConf.AVRO_SCHEMA_URL_ALLOWED_SCHEMES, Seq("file"))
+    SQLConf.withExistingConf(conf) {
+      val e = intercept[AnalysisException] {
+        new AvroOptions(Map("avroSchemaUrl" -> "S3A://bucket/user.avsc"), 
hadoopConf)
+      }
+      assert(e.getCondition == "STDS_INVALID_OPTION_VALUE.WITH_MESSAGE")
+      assert(e.getMessage.contains("avroSchemaUrl"))
+      assert(e.getMessage.contains("not in the allowlist"))
+      assert(e.getMessage.contains("The scheme 's3a'"))

Review Comment:
   Added in d325505: `AvroSuite` "SPARK-59329: the allowlist rejection echoes 
the parsed allowlist" uses a `file://` entry (which parses to one entry that 
matches nothing) and asserts the message contains both `The scheme 'file'` and 
`not in the allowlist [file://]`, so dropping the parsed-allowlist echo fails 
it. Runs green on head; the second assertion is exactly what pins the mutation 
you measured.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to