From f65d4457519019cbf850f4ff2cda223fca650e92 Mon Sep 17 00:00:00 2001 From: rei miyasaka Date: Fri, 12 Jan 2024 17:55:52 -0800 Subject: [PATCH 1/4] quoted passwords with tests --- src-tests/data/test6a.netrc | 1 + src-tests/data/test6a.netrc.out | 1 + src-tests/data/test6a.netrc.out2 | 1 + src-tests/data/test6b.netrc | 1 + src-tests/data/test6b.netrc.out | 1 + src-tests/data/test6b.netrc.out2 | 1 + src-tests/data/test6c.netrc | 1 + src-tests/data/test6c.netrc.out | 1 + src-tests/data/test6c.netrc.out2 | 1 + src-tests/data/test6d.netrc | 1 + src-tests/data/test6d.netrc.out | 1 + src-tests/data/test6d.netrc.out2 | 1 + src-tests/data/test6e.netrc | 1 + src-tests/data/test6e.netrc.out | 1 + src-tests/data/test6e.netrc.out2 | 1 + src-tests/data/test6f.netrc | 1 + src-tests/data/test6f.netrc.out | 1 + src-tests/data/test6f.netrc.out2 | 1 + src-tests/data/test6g.netrc | 1 + src-tests/data/test6g.netrc.out | 1 + src-tests/data/test6g.netrc.out2 | 1 + src/Network/NetRc.hs | 15 ++++++++++++++- 22 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src-tests/data/test6a.netrc create mode 100644 src-tests/data/test6a.netrc.out create mode 100644 src-tests/data/test6a.netrc.out2 create mode 100644 src-tests/data/test6b.netrc create mode 100644 src-tests/data/test6b.netrc.out create mode 100644 src-tests/data/test6b.netrc.out2 create mode 100644 src-tests/data/test6c.netrc create mode 100644 src-tests/data/test6c.netrc.out create mode 100644 src-tests/data/test6c.netrc.out2 create mode 100644 src-tests/data/test6d.netrc create mode 100644 src-tests/data/test6d.netrc.out create mode 100644 src-tests/data/test6d.netrc.out2 create mode 100644 src-tests/data/test6e.netrc create mode 100644 src-tests/data/test6e.netrc.out create mode 100644 src-tests/data/test6e.netrc.out2 create mode 100644 src-tests/data/test6f.netrc create mode 100644 src-tests/data/test6f.netrc.out create mode 100644 src-tests/data/test6f.netrc.out2 create mode 100644 src-tests/data/test6g.netrc create mode 100644 src-tests/data/test6g.netrc.out create mode 100644 src-tests/data/test6g.netrc.out2 diff --git a/src-tests/data/test6a.netrc b/src-tests/data/test6a.netrc new file mode 100644 index 0000000..2ad51ac --- /dev/null +++ b/src-tests/data/test6a.netrc @@ -0,0 +1 @@ +default login abc password """" \ No newline at end of file diff --git a/src-tests/data/test6a.netrc.out b/src-tests/data/test6a.netrc.out new file mode 100644 index 0000000..51f608b --- /dev/null +++ b/src-tests/data/test6a.netrc.out @@ -0,0 +1 @@ +Right (NetRc {nrHosts = [NetRcHost {nrhName = "", nrhLogin = "abc", nrhPassword = "", nrhAccount = "", nrhMacros = []}], nrMacros = []}) \ No newline at end of file diff --git a/src-tests/data/test6a.netrc.out2 b/src-tests/data/test6a.netrc.out2 new file mode 100644 index 0000000..50a229b --- /dev/null +++ b/src-tests/data/test6a.netrc.out2 @@ -0,0 +1 @@ +default login abc diff --git a/src-tests/data/test6b.netrc b/src-tests/data/test6b.netrc new file mode 100644 index 0000000..218359d --- /dev/null +++ b/src-tests/data/test6b.netrc @@ -0,0 +1 @@ +default login abc password ""multi-word password"" \ No newline at end of file diff --git a/src-tests/data/test6b.netrc.out b/src-tests/data/test6b.netrc.out new file mode 100644 index 0000000..2950437 --- /dev/null +++ b/src-tests/data/test6b.netrc.out @@ -0,0 +1 @@ +Right (NetRc {nrHosts = [NetRcHost {nrhName = "", nrhLogin = "abc", nrhPassword = "multi-word password", nrhAccount = "", nrhMacros = []}], nrMacros = []}) \ No newline at end of file diff --git a/src-tests/data/test6b.netrc.out2 b/src-tests/data/test6b.netrc.out2 new file mode 100644 index 0000000..9b2f004 --- /dev/null +++ b/src-tests/data/test6b.netrc.out2 @@ -0,0 +1 @@ +default login abc password multi-word password diff --git a/src-tests/data/test6c.netrc b/src-tests/data/test6c.netrc new file mode 100644 index 0000000..a34d2aa --- /dev/null +++ b/src-tests/data/test6c.netrc @@ -0,0 +1 @@ +default login abc password ""unescape \\ backslash"" \ No newline at end of file diff --git a/src-tests/data/test6c.netrc.out b/src-tests/data/test6c.netrc.out new file mode 100644 index 0000000..5896a09 --- /dev/null +++ b/src-tests/data/test6c.netrc.out @@ -0,0 +1 @@ +Right (NetRc {nrHosts = [NetRcHost {nrhName = "", nrhLogin = "abc", nrhPassword = "unescape \\ backslash", nrhAccount = "", nrhMacros = []}], nrMacros = []}) \ No newline at end of file diff --git a/src-tests/data/test6c.netrc.out2 b/src-tests/data/test6c.netrc.out2 new file mode 100644 index 0000000..da9537a --- /dev/null +++ b/src-tests/data/test6c.netrc.out2 @@ -0,0 +1 @@ +default login abc password unescape \ backslash diff --git a/src-tests/data/test6d.netrc b/src-tests/data/test6d.netrc new file mode 100644 index 0000000..133aab7 --- /dev/null +++ b/src-tests/data/test6d.netrc @@ -0,0 +1 @@ +default login abc password ""unescape \" quote"" \ No newline at end of file diff --git a/src-tests/data/test6d.netrc.out b/src-tests/data/test6d.netrc.out new file mode 100644 index 0000000..f2ee0e6 --- /dev/null +++ b/src-tests/data/test6d.netrc.out @@ -0,0 +1 @@ +Right (NetRc {nrHosts = [NetRcHost {nrhName = "", nrhLogin = "abc", nrhPassword = "unescape \" quote", nrhAccount = "", nrhMacros = []}], nrMacros = []}) \ No newline at end of file diff --git a/src-tests/data/test6d.netrc.out2 b/src-tests/data/test6d.netrc.out2 new file mode 100644 index 0000000..7a60a40 --- /dev/null +++ b/src-tests/data/test6d.netrc.out2 @@ -0,0 +1 @@ +default login abc password unescape " quote diff --git a/src-tests/data/test6e.netrc b/src-tests/data/test6e.netrc new file mode 100644 index 0000000..1bfe2df --- /dev/null +++ b/src-tests/data/test6e.netrc @@ -0,0 +1 @@ +default login abc password ""unclosed-double-quotes \ No newline at end of file diff --git a/src-tests/data/test6e.netrc.out b/src-tests/data/test6e.netrc.out new file mode 100644 index 0000000..411a36f --- /dev/null +++ b/src-tests/data/test6e.netrc.out @@ -0,0 +1 @@ +Right (NetRc {nrHosts = [NetRcHost {nrhName = "", nrhLogin = "abc", nrhPassword = "\"\"unclosed-double-quotes", nrhAccount = "", nrhMacros = []}], nrMacros = []}) \ No newline at end of file diff --git a/src-tests/data/test6e.netrc.out2 b/src-tests/data/test6e.netrc.out2 new file mode 100644 index 0000000..e8ff760 --- /dev/null +++ b/src-tests/data/test6e.netrc.out2 @@ -0,0 +1 @@ +default login abc password ""unclosed-double-quotes diff --git a/src-tests/data/test6f.netrc b/src-tests/data/test6f.netrc new file mode 100644 index 0000000..0bd530b --- /dev/null +++ b/src-tests/data/test6f.netrc @@ -0,0 +1 @@ +default login abc password ""password with # comment"" \ No newline at end of file diff --git a/src-tests/data/test6f.netrc.out b/src-tests/data/test6f.netrc.out new file mode 100644 index 0000000..4c3635c --- /dev/null +++ b/src-tests/data/test6f.netrc.out @@ -0,0 +1 @@ +Right (NetRc {nrHosts = [NetRcHost {nrhName = "", nrhLogin = "abc", nrhPassword = "password with # comment", nrhAccount = "", nrhMacros = []}], nrMacros = []}) \ No newline at end of file diff --git a/src-tests/data/test6f.netrc.out2 b/src-tests/data/test6f.netrc.out2 new file mode 100644 index 0000000..1d28e5f --- /dev/null +++ b/src-tests/data/test6f.netrc.out2 @@ -0,0 +1 @@ +default login abc password password with # comment diff --git a/src-tests/data/test6g.netrc b/src-tests/data/test6g.netrc new file mode 100644 index 0000000..0c23ab9 --- /dev/null +++ b/src-tests/data/test6g.netrc @@ -0,0 +1 @@ +default login abc password "single # quote should be treated as normal password" \ No newline at end of file diff --git a/src-tests/data/test6g.netrc.out b/src-tests/data/test6g.netrc.out new file mode 100644 index 0000000..57e066f --- /dev/null +++ b/src-tests/data/test6g.netrc.out @@ -0,0 +1 @@ +Right (NetRc {nrHosts = [NetRcHost {nrhName = "", nrhLogin = "abc", nrhPassword = "\"single", nrhAccount = "", nrhMacros = []}], nrMacros = []}) \ No newline at end of file diff --git a/src-tests/data/test6g.netrc.out2 b/src-tests/data/test6g.netrc.out2 new file mode 100644 index 0000000..784d7b7 --- /dev/null +++ b/src-tests/data/test6g.netrc.out2 @@ -0,0 +1 @@ +default login abc password "single diff --git a/src/Network/NetRc.hs b/src/Network/NetRc.hs index 7b68a0b..ef56168 100644 --- a/src/Network/NetRc.hs +++ b/src/Network/NetRc.hs @@ -238,10 +238,14 @@ hostEnt = do -- pval := ((account|username|password) WS+ ) pval = hlp "login" PValLogin <|> hlp "account" PValAccount <|> - hlp "password" PValPassword + hlpPassword where hlp tnam cons = P.try (P.string tnam) *> wsChars1 *> (cons <$> tok P. (tnam ++ "-value")) + hlpPassword = P.try (P.string "password") *> wsChars1 *> + (PValPassword <$> password P. "password-value") + password = P.try quotedPassword <|> tok + setFld n (PValLogin v) = n { nrhLogin = v } setFld n (PValAccount v) = n { nrhAccount = v } @@ -250,6 +254,14 @@ hostEnt = do tok :: P.Parser ByteString tok = BC.pack <$> P.many1 notWsChar P. "token" +quotedPassword :: P.Parser ByteString +quotedPassword = do + BC.pack <$> (P.string "\"\"" *> P.many chars <* P.string "\"\"") + where + chars = escaped <|> P.noneOf "\"" + escaped = P.char '\\' >> P.choice [ P.char '\\' >> return '\\' + , P.char '"' >> return '"' ] + data PVal = PValLogin !ByteString | PValAccount !ByteString | PValPassword !ByteString @@ -288,3 +300,4 @@ splitEithers = goL isLeft (Right _) = False isRight = not . isLeft + \ No newline at end of file From 59eb4329417bedc8be51407a5cc1815e76396fce Mon Sep 17 00:00:00 2001 From: rei miyasaka Date: Fri, 12 Jan 2024 18:11:10 -0800 Subject: [PATCH 2/4] Double quotes comment in blurb --- src/Network/NetRc.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Network/NetRc.hs b/src/Network/NetRc.hs index ef56168..a36a303 100644 --- a/src/Network/NetRc.hs +++ b/src/Network/NetRc.hs @@ -27,6 +27,10 @@ -- and after @machine@\/@default@\/@macdef@ entries. Be aware though -- that such @#@-comment are not supported by all @.netrc@-aware -- applications, including @ftp(1)@. +-- +-- Passwords can contain spaces only if they are double-quoted, in which +-- case backslashes and quotes are to be escaped with a preceding backslash. + module Network.NetRc ( -- * Types NetRc(..) From 25cb85fb88ed449c76d129424c72d2bda2869155 Mon Sep 17 00:00:00 2001 From: rei miyasaka Date: Sat, 13 Jan 2024 01:30:50 -0800 Subject: [PATCH 3/4] Add quoted password support to netRcToBuilder --- src-tests/data/test6b.netrc.out2 | 2 +- src-tests/data/test6c.netrc.out2 | 2 +- src-tests/data/test6d.netrc.out2 | 2 +- src-tests/data/test6f.netrc.out2 | 2 +- src/Network/NetRc.hs | 14 +++++++++++++- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src-tests/data/test6b.netrc.out2 b/src-tests/data/test6b.netrc.out2 index 9b2f004..ee1cb80 100644 --- a/src-tests/data/test6b.netrc.out2 +++ b/src-tests/data/test6b.netrc.out2 @@ -1 +1 @@ -default login abc password multi-word password +default login abc password ""multi-word password"" diff --git a/src-tests/data/test6c.netrc.out2 b/src-tests/data/test6c.netrc.out2 index da9537a..68d2c5e 100644 --- a/src-tests/data/test6c.netrc.out2 +++ b/src-tests/data/test6c.netrc.out2 @@ -1 +1 @@ -default login abc password unescape \ backslash +default login abc password ""unescape \\ backslash"" diff --git a/src-tests/data/test6d.netrc.out2 b/src-tests/data/test6d.netrc.out2 index 7a60a40..c7d9805 100644 --- a/src-tests/data/test6d.netrc.out2 +++ b/src-tests/data/test6d.netrc.out2 @@ -1 +1 @@ -default login abc password unescape " quote +default login abc password ""unescape \" quote"" diff --git a/src-tests/data/test6f.netrc.out2 b/src-tests/data/test6f.netrc.out2 index 1d28e5f..4b4b1bb 100644 --- a/src-tests/data/test6f.netrc.out2 +++ b/src-tests/data/test6f.netrc.out2 @@ -1 +1 @@ -default login abc password password with # comment +default login abc password ""password with # comment"" diff --git a/src/Network/NetRc.hs b/src/Network/NetRc.hs index a36a303..765e462 100644 --- a/src/Network/NetRc.hs +++ b/src/Network/NetRc.hs @@ -123,7 +123,7 @@ netRcToBuilder (NetRc ms ds) = = mconcat $ [ mline , prop "login" nrhLogin - , prop "password" nrhPassword + , propPassword nrhPassword , prop "account" nrhAccount , nl ] <> (intersperse nl $ map netRcMacDefToBuilder nrhMacros) @@ -133,6 +133,18 @@ netRcToBuilder (NetRc ms ds) = prop lab val | B.null val = mempty | otherwise = spc <> BB.byteString lab <> spc <> BB.byteString val + propPassword val | B.null val = mempty + | otherwise = spc <> BB.byteString "password" <> spc <> passwordValString val + passwordValString :: ByteString -> BB.Builder + passwordValString val = + if B.any (\c -> c == charToW8 ' ' || c == charToW8 '\t') val then + BB.byteString "\"\"" <> BB.byteString (BC.concatMap (BC.pack . escape) val) <> BB.byteString "\"\"" + else BB.byteString val + where + escape '\\' = "\\\\" + escape '\"' = "\\\"" + escape x = [ x ] + charToW8 = toEnum . fromEnum netRcMacDefToBuilder (NetRcMacDef {..}) = BB.byteString "macdef" <> spc <> BB.byteString nrmName <> From 604c311f71d7847bc04c7e54b5b8f688fab97831 Mon Sep 17 00:00:00 2001 From: rei miyasaka Date: Sat, 13 Jan 2024 02:12:58 -0800 Subject: [PATCH 4/4] netRcToBuilder refactoring --- src/Network/NetRc.hs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Network/NetRc.hs b/src/Network/NetRc.hs index 765e462..6d795c5 100644 --- a/src/Network/NetRc.hs +++ b/src/Network/NetRc.hs @@ -123,7 +123,7 @@ netRcToBuilder (NetRc ms ds) = = mconcat $ [ mline , prop "login" nrhLogin - , propPassword nrhPassword + , prop "password" nrhPassword , prop "account" nrhAccount , nl ] <> (intersperse nl $ map netRcMacDefToBuilder nrhMacros) @@ -132,19 +132,19 @@ netRcToBuilder (NetRc ms ds) = | otherwise = BB.byteString "machine" <> spc <> BB.byteString nrhName prop lab val | B.null val = mempty - | otherwise = spc <> BB.byteString lab <> spc <> BB.byteString val - propPassword val | B.null val = mempty - | otherwise = spc <> BB.byteString "password" <> spc <> passwordValString val - passwordValString :: ByteString -> BB.Builder - passwordValString val = - if B.any (\c -> c == charToW8 ' ' || c == charToW8 '\t') val then - BB.byteString "\"\"" <> BB.byteString (BC.concatMap (BC.pack . escape) val) <> BB.byteString "\"\"" - else BB.byteString val + | otherwise = spc <> BB.byteString lab <> spc <> valString lab val + + valString "password" val + | BC.elem ' ' val || BC.elem '\t' val + = BB.byteString "\"\"" + <> BB.byteString (BC.concatMap (BC.pack . escape) val) + <> BB.byteString "\"\"" + | otherwise = BB.byteString val where escape '\\' = "\\\\" escape '\"' = "\\\"" escape x = [ x ] - charToW8 = toEnum . fromEnum + valString _ val = BB.byteString val netRcMacDefToBuilder (NetRcMacDef {..}) = BB.byteString "macdef" <> spc <> BB.byteString nrmName <>