A scheme or workspace with a control character in a value is written as malformed XML, and XcodeProj can't read it back
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 85/100
Direzione di ricerca
Inizia da Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift e dall’escaping degli attributi e dei valori stringa in xmlXcodeFormat. L’issue descrive test in Tests/XcodeProjTests/Extensions/XcodeFormatForbiddenCharacterTests.swift; usali per verificare i caratteri di controllo vietati, quelli validi e i round-trip di scheme/workspace. Il lavoro è completato quando l’XML scritto rimane leggibile e l’intera suite di test swift test ha esito positivo.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Context 🕵️♀️
XCScheme.write(path:override:), XCWorkspaceData.write(path:override:) and their dataRepresentation() render
through AEXMLDocument.xmlXcodeFormat (Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift). It escapes every
attribute value with AEXML's String.xmlEscaped, which handles &, <, >, ', ", \n, \r and \t. XML 1.0
also forbids, outright, the other C0 controls (U+0001–U+0008, U+000B, U+000C, U+000E–U+001F) and U+FFFE and U+FFFF.
No escape can write them;  is malformed too. xmlEscaped copies them through, so the file XcodeProj writes is
not well-formed, and XCScheme(path:) / XCWorkspaceData(path:) throw on it.
The values come from callers: an environment variable's value, a pre- or post-action's scriptText, a command-line
argument, a file reference's path. Tools that generate schemes from a manifest pass such strings straight through.
#530 is an XcodeGen pre-action script that reached this writer. A terminal colour code pasted into a value, with a
raw ESC (U+001B), is the likeliest way in.
What 🌱
import PathKit
import XcodeProj
let launch = XCScheme.LaunchAction(
runnable: nil,
buildConfiguration: "Debug",
environmentVariables: [XCScheme.EnvironmentVariable(variable: "PROMPT", value: "\u{1B}[31m$ \u{1B}[0m", enabled: true)]
)
let path = Path("App.xcscheme")
try XCScheme(name: "App", lastUpgradeVersion: "2600", version: "1.7", launchAction: launch).write(path: path, override: true)
_ = try XCScheme(path: path)
// throws: Error Domain=NSXMLParserErrorDomain Code=9 "(null)"
// UserInfo={NSXMLParserErrorLineNumber=18, NSXMLParserErrorColumn=22, NSXMLParserErrorMessage=invalid character in attribute value}
Line 18 of App.xcscheme, through cat -v: value = "^[[31m$ ^[[0m". A workspace does the same: a FileRef at
.group("a\u{1}b.swift") is written as location = "group:a^Ab.swift", and XCWorkspaceData(path:) throws code 9
at line 5. With "red $ " and "ab.swift" instead, both files read back. Every one of the 30 code points fails the
same way.
Proposal 🎉
Replace each of those characters with U+FFFD in xmlXcodeFormat, after xmlEscaped:
--- a/Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift
+++ b/Sources/XcodeProj/Extensions/AEXML+XcodeFormat.swift
@@ -124,7 +124,7 @@ extension AEXMLElement {
func print(key: String, value: String) {
xml += "\n"
xml += indent(withDepth: parentsCount)
- xml += "\(key) = \"\(value.xmlEscaped)\""
+ xml += "\(key) = \"\(value.xcodeFormatEscaped)\""
}
if !attributes.isEmpty {
@@ -156,7 +156,7 @@ extension AEXMLElement {
// insert string value and close element
xml += ">\n"
xml += indent(withDepth: parentsCount - 1)
- xml += ">\n\(string.xmlEscaped)"
+ xml += ">\n\(string.xcodeFormatEscaped)"
}
}
@@ -190,3 +190,21 @@ extension AEXMLElement {
return indent
}
}
+
+extension String {
+ /// `xmlEscaped`, with U+FFFD in place of each character XML 1.0 cannot hold at all, even as a reference:
+ /// a C0 control other than tab, line feed and carriage return, U+FFFE or U+FFFF. A file holding one is not
+ /// well-formed, so neither XcodeProj nor any other XML reader can load it.
+ fileprivate var xcodeFormatEscaped: String {
+ var scalars = String.UnicodeScalarView()
+ for scalar in xmlEscaped.unicodeScalars {
+ switch scalar {
+ case "\u{0}"..."\u{8}", "\u{B}", "\u{C}", "\u{E}"..."\u{1F}", "\u{FFFE}", "\u{FFFF}":
+ scalars.append("\u{FFFD}")
+ default:
+ scalars.append(scalar)
+ }
+ }
+ return String(scalars)
+ }
+}
This loses the character, but the file stays loadable. The alternative is to throw from write and
dataRepresentation, which would mean xmlXcodeFormat throwing too. Output without these characters is unchanged.
I have an XCTest file, Tests/XcodeProjTests/Extensions/XcodeFormatForbiddenCharacterTests.swift, with four tests:
the scheme and the workspace above, all 30 code points in an attribute value, and a control (&, <, >, quotes,
tab and newline, which round-trip today). On main the three tests fail (32 assertions) and the control passes.
With the patch all four pass, and so does the full swift test: 387 XCTest and 95 Swift Testing tests. Happy to
open a PR with both.
AEXML's own xml output has the same gap in String.xmlEscaped (4.7.3, unchanged on its master). I have not
reported it there.
Environment. XcodeProj main at 948280a (AEXML 4.7.3). Xcode 27.2 (Swift 6.4.2), macOS 27.2, arm64.
Found while checking whether XML written by Swift packages parses.
- Lingua principale
- Swift
- Stelle
- 2.2k
- Fork
- 356
- Merge medio
- 5h 54m
- PR unite (30g)
- 8
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Nessuna guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di tuist/XcodeProj
-
New Xcode 26 formatAperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 52/100
-
Removing a target referenced by PBXFileSystemSynchronizedBuildFileExceptionSet crashes serializationAperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 55/100
-
PBXProjEncoder crashes when sorting projectReferences if ProjectRef file element has nil nameForse già presa @Ant-tree l’ha presa 262 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 35/100
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 45/100
Tutte le issue di tuist/XcodeProj
Issue simili
-
🎉 Add BossanovaAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
jaywcjlove/awesome-mac#4096 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
I maintainer di solito rispondono entro 1 giorno
-
[BUG] FluidMeet: Google Meet browser calls not detected after Meet moved rooms to /meet/<code>Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
altic-dev/FluidVoice#1264 ·
I maintainer di solito rispondono entro 1 giorno
-
needs-triage S3: minor
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
manaflow-ai/cmux#28422 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
`Heap._checkInvariants()` is `internal` under `COLLECTIONS_INTERNAL_CHECKS` but `public` otherwiseAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
apple/swift-collections#813 ·
I maintainer di solito rispondono entro 1 giorno