packages feed

idn 0.1.1.0 → 0.1.2.0

raw patch · 4 files changed

+28/−22 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

idn.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: idn-version: 0.1.1.0+version: 0.1.2.0 synopsis: Pure Haskell IDN and Punycode implementation description:      Pure Haskell implementation of Internationalized Domain Names (IDN)
src/Data/Text/IDN.hs view
@@ -50,14 +50,14 @@ -- -- Converts each label in the domain name to its ASCII-compatible form. -- Unicode labels are encoded using Punycode and prefixed with "xn--".--- ASCII labels are validated and passed through unchanged.+-- ASCII labels are normalized to lowercase (case-insensitive per DNS). -- -- === Examples -- -- >>> toASCII "münchen.de" -- Right "xn--mnchen-3ya.de" ----- >>> toASCII "example.com"+-- >>> toASCII "EXAMPLE.COM" -- Right "example.com" -- -- === Errors@@ -113,6 +113,7 @@ -- | Process a single label to ASCII form. processLabelToASCII :: Label -> Either IDNError Label processLabelToASCII (ALabel t)+  -- Labels are already normalized to lowercase by mkLabel   | "xn--" `T.isPrefixOf` t = do       -- It's a Punycode A-label, validate it by decoding and re-encoding       -- This uses processLabelToUnicode which validates Punycode properly@@ -120,12 +121,12 @@       -- Then re-process back to ASCII to get a validated A-label       processLabelToASCII ulabel   | otherwise =-      -- Regular ASCII label, just validate+      -- Regular ASCII label (already normalized), just validate       validateLabel t >> return (ALabel t)-  + processLabelToASCII (ULabel t)   | isAsciiLabel t = do-      -- Pure ASCII label, validate and return as-is+      -- Pure ASCII label (already normalized), just validate       validateLabel t       return (ULabel t)   @@ -144,22 +145,19 @@  -- | Process a single label to Unicode form. processLabelToUnicode :: Label -> Either IDNError Label-processLabelToUnicode (ULabel t) = +processLabelToUnicode (ULabel t) =   -- Already Unicode, validate and return   validateLabel t >> return (ULabel t)-  + processLabelToUnicode (ALabel t)-  | "xn--" `T.isPrefixOf` t = do+  -- RFC 5891 Section 5.1: A-labels are case-insensitive for matching+  | "xn--" `T.isPrefixOf` T.toLower t = do       -- Punycode A-label, decode it-      -- RFC 5891 Section 5.4: A-labels must be lowercase+      -- RFC 5891 Section 5.1: Accept case-insensitive input, normalize to lowercase+      -- Drop the prefix (which might be XN--, Xn--, xN--, or xn--)       let encoded = T.drop 4 t           encodedLower = T.toLower encoded -      -- Check if original was already lowercase (uppercase in Punycode is invalid)-      if encoded /= encodedLower-        then Left (PunycodeDecodeError encoded "A-label must be lowercase")-        else pure ()-       decoded <- case decodePunycode encodedLower of         Left err -> Left (PunycodeDecodeError encoded (T.pack (show err)))         Right d -> Right d@@ -246,13 +244,15 @@   case T.uncons label of     Just ('-', _) -> Left (InvalidHyphenPosition StartsWithHyphen)     _ -> pure ()-  +   case T.unsnoc label of     Just (_, '-') -> Left (InvalidHyphenPosition EndsWithHyphen)     _ -> pure ()-  +   -- Check for "--" in positions 3-4 (unless it's an A-label with xn--)-  if T.length label >= 4 && not ("xn--" `T.isPrefixOf` label)+  -- RFC 5891: A-labels are case-insensitive for matching+  let labelLower = T.toLower label+  if T.length label >= 4 && not ("xn--" `T.isPrefixOf` labelLower)     then if T.index label 2 == '-' && T.index label 3 == '-'       then Left (InvalidHyphenPosition HyphensAt3And4NotPunycode)       else pure ()
src/Data/Text/IDN/Types.hs view
@@ -185,8 +185,13 @@ mkLabel text   | T.null text = Left EmptyLabel   | T.length text > 63 = Left (LabelTooLong (T.length text) 63)-  | "xn--" `T.isPrefixOf` text = Right (ALabel text)-  | otherwise = Right (ULabel text)+  | otherwise =+      -- RFC 5891/3490: Domain labels are case-insensitive, normalize to lowercase+      let normalized = T.toLower text+      -- RFC 5891 Section 5.1: A-labels start with "xn--"+      in if "xn--" `T.isPrefixOf` normalized+           then Right (ALabel normalized)+           else Right (ULabel normalized)  -- | Smart constructor for 'DomainName' with invariant enforcement. --
test/Data/Text/IDNSpec.hs view
@@ -54,8 +54,9 @@       toASCII "example-.com" `shouldSatisfy` isLeft       toASCII "ab--cd.com" `shouldSatisfy` isLeft     -    it "accepts valid xn-- Punycode prefix" $ do-      toASCII "xn--test.com" `shouldSatisfy` isRight+    it "rejects xn-- prefix with pure ASCII (RFC 5891 violation)" $ do+      -- RFC 5891: Punycode should only be used for labels with non-ASCII characters+      toASCII "xn--test.com" `shouldSatisfy` isLeft          it "handles multi-label domains" $ do       toASCII "sub.example.com" `shouldBe` Right "sub.example.com"