Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Sources/CodexBarCLI/CLIConfigCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,7 @@ extension CodexBarCLI {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
value = value.trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
Expand Down
3 changes: 1 addition & 2 deletions Sources/CodexBarCore/Config/CodexBarConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,7 @@ public struct ProviderConfig: Codable, Sendable, Identifiable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
value = value.trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ public struct AlibabaCodingPlanSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ public struct AlibabaTokenPlanSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
value = value.trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ public enum AzureOpenAISettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ public enum BedrockSettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ public enum ClaudeAdminAPISettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ public enum CodebuffSettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
3 changes: 1 addition & 2 deletions Sources/CodexBarCore/Providers/Crof/CrofSettingsReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ public enum CrofSettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
value = value.trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ public struct DeepSeekSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
return value.trimmingCharacters(in: .whitespacesAndNewlines)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ public struct DeepgramSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ public struct DoubaoSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
return value.trimmingCharacters(in: .whitespacesAndNewlines)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ public enum ElevenLabsSettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
value = value.trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
Expand Down
3 changes: 1 addition & 2 deletions Sources/CodexBarCore/Providers/Groq/GroqSettingsReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ public enum GroqSettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
value = value.trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
Expand Down
3 changes: 1 addition & 2 deletions Sources/CodexBarCore/Providers/Kilo/KiloSettingsReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ public enum KiloSettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
3 changes: 1 addition & 2 deletions Sources/CodexBarCore/Providers/Kimi/KimiSettingsReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ public enum KimiSettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ public struct KimiK2SettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
return value.trimmingCharacters(in: .whitespacesAndNewlines)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ public enum LLMProxySettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
value = value.trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ public enum ManusSettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ public struct MiniMaxAPISettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ public struct MiniMaxSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ public struct MoonshotSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\""))
|| (value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
return value.trimmingCharacters(in: .whitespacesAndNewlines)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -651,8 +651,7 @@ public struct OllamaAPISettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
return value.trimmingCharacters(in: .whitespacesAndNewlines)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ public enum OpenAIAPISettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ public enum OpenRouterSettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ public enum PerplexitySettingsReader {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
3 changes: 1 addition & 2 deletions Sources/CodexBarCore/Providers/ProviderTokenResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,7 @@ public enum ProviderTokenResolver {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ public struct StepFunSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
value = value.trimmingCharacters(in: .whitespacesAndNewlines)
return value.isEmpty ? nil : value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ public struct SyntheticSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ public struct VeniceSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
return value.trimmingCharacters(in: .whitespacesAndNewlines)
}
Expand Down
3 changes: 1 addition & 2 deletions Sources/CodexBarCore/Providers/Warp/WarpSettingsReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ public struct WarpSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}
return value.trimmingCharacters(in: .whitespacesAndNewlines)
}
Expand Down
3 changes: 1 addition & 2 deletions Sources/CodexBarCore/Providers/Zai/ZaiSettingsReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ public struct ZaiSettingsReader: Sendable {
if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
(value.hasPrefix("'") && value.hasSuffix("'"))
{
value.removeFirst()
value.removeLast()
value = String(value.dropFirst().dropLast())
}

value = value.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
75 changes: 75 additions & 0 deletions TestsLinux/SettingsReaderQuoteUnwrapTrapTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import CodexBarCore
import Foundation
import Testing

/// Regression tests for a copy-pasted quote-unwrap helper that traps on length-1 input.
///
/// 32 provider settings readers (plus `CodexBarConfig` and `CLIConfigCommand`) share a
/// `cleaned(_:)` helper of the form:
///
/// if (value.hasPrefix("\"") && value.hasSuffix("\"")) ||
/// (value.hasPrefix("'") && value.hasSuffix("'"))
/// {
/// value.removeFirst()
/// value.removeLast()
/// }
///
/// For a value of length 1 (the single character `"` or `'`), both `hasPrefix` and
/// `hasSuffix` return true, `removeFirst()` empties the string, and `removeLast()` then
/// traps with "Can't remove last element from empty collection." This is reachable from
/// a misconfigured env var (e.g. `ALIBABA_TOKEN_PLAN_COOKIE='"'`) and from quoted JSON
/// values in `~/.codexbar/config.json`, both of which are user-controllable.
///
/// These tests exercise two representative public readers — Alibaba Token Plan (the
/// newest addition in #1098) and the Ollama API key reader (added in #1087) — by
/// passing the trap-inducing single-quote inputs and asserting the readers return nil
/// instead of crashing. The patch swaps `removeFirst()/removeLast()` for
/// `String(value.dropFirst().dropLast())`, which is empty-safe.
@Suite
struct SettingsReaderQuoteUnwrapTrapTests {
@Test
func alibabaTokenPlanCookieHeader_returnsNilForLoneDoubleQuoteValue() {
let env = [AlibabaTokenPlanSettingsReader.cookieHeaderKey: "\""]
#expect(AlibabaTokenPlanSettingsReader.cookieHeader(environment: env) == nil)
}

@Test
func alibabaTokenPlanCookieHeader_returnsNilForLoneApostropheValue() {
let env = [AlibabaTokenPlanSettingsReader.cookieHeaderKey: "'"]
#expect(AlibabaTokenPlanSettingsReader.cookieHeader(environment: env) == nil)
}

@Test
func alibabaTokenPlanCookieHeader_unwrapsProperlyDoubleQuotedValue() {
let env = [AlibabaTokenPlanSettingsReader.cookieHeaderKey: "\"abc=def\""]
#expect(AlibabaTokenPlanSettingsReader.cookieHeader(environment: env) == "abc=def")
}

@Test
func alibabaTokenPlanCookieHeader_unwrapsProperlySingleQuotedValue() {
let env = [AlibabaTokenPlanSettingsReader.cookieHeaderKey: "'abc=def'"]
#expect(AlibabaTokenPlanSettingsReader.cookieHeader(environment: env) == "abc=def")
}

@Test
func ollamaAPIKey_returnsNilForLoneDoubleQuoteValue() {
for key in OllamaAPISettingsReader.apiKeyEnvironmentKeys {
let env = [key: "\""]
#expect(OllamaAPISettingsReader.apiKey(environment: env) == nil)
}
}

@Test
func ollamaAPIKey_returnsNilForLoneApostropheValue() {
for key in OllamaAPISettingsReader.apiKeyEnvironmentKeys {
let env = [key: "'"]
#expect(OllamaAPISettingsReader.apiKey(environment: env) == nil)
}
}

@Test
func ollamaAPIKey_unwrapsProperlyQuotedValue() {
let env = [OllamaAPISettingsReader.apiKeyEnvironmentKeys[0]: "\"sk-token\""]
#expect(OllamaAPISettingsReader.apiKey(environment: env) == "sk-token")
}
}
Loading