Skip to content

Commit

Permalink
Deprecate TaintedString (#15423)
Browse files Browse the repository at this point in the history
Co-authored-by: Timothee Cour <[email protected]>
Co-authored-by: Dominik Picheta <[email protected]>
  • Loading branch information
3 people authored Jan 16, 2021
1 parent 7b632f9 commit 78a9958
Show file tree
Hide file tree
Showing 36 changed files with 184 additions and 229 deletions.
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior.

- Added `--declaredlocs` to show symbol declaration location in messages.

- Deprecated `TaintedString` and `--taintmode`.

- Source+Edit links now appear on top of every docgen'd page when
`nim doc --git.url:url ...` is given.

Expand Down
4 changes: 2 additions & 2 deletions compiler/commands.nim
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
of "symbolfiles": result = conf.symbolFiles != disabledSf
of "genscript": result = contains(conf.globalOptions, optGenScript)
of "threads": result = contains(conf.globalOptions, optThreads)
of "taintmode": result = contains(conf.globalOptions, optTaintMode)
of "taintmode": result = false # pending https://github.com/nim-lang/Nim/issues/16731
of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
of "implicitstatic": result = contains(conf.options, optImplicitStatic)
of "patterns", "trmacros": result = contains(conf.options, optTrMacros)
Expand Down Expand Up @@ -670,7 +670,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
#if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info)
of "taintmode": discard # pending https://github.com/nim-lang/Nim/issues/16731
of "implicitstatic":
processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
of "patterns", "trmacros":
Expand Down
1 change: 0 additions & 1 deletion compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ type # please make sure we have under 32 options
optThreads, # support for multi-threading
optStdout, # output to stdout
optThreadAnalysis, # thread analysis pass
optTaintMode, # taint mode turned on
optTlsEmulation, # thread var emulation turned on
optGenIndex # generate index file for documentation;
optEmbedOrigSrc # embed the original source in the generated code
Expand Down
1 change: 0 additions & 1 deletion doc/advopt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ Advanced options:
in the generated output
--threadanalysis:on|off turn thread analysis on|off
--tlsEmulation:on|off turn thread local storage emulation on|off
--taintMode:on|off turn taint mode on|off
--implicitStatic:on|off turn implicit compile time evaluation on|off
--trmacros:on|off turn term rewriting macros on|off
--multimethods:on|off turn multi-methods on|off
Expand Down
2 changes: 1 addition & 1 deletion doc/idetools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ skLet
let
text = "some text"
--> col 2: $MODULE.text
col 3: TaintedString
col 3: string
col 7: ""
Expand Down
22 changes: 0 additions & 22 deletions doc/manual_experimental.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1718,28 +1718,6 @@ e.g. with given example ``echo("ab")`` will be rewritten just once:
``noRewrite`` pragma can be useful to control term-rewriting macros recursion.


Taint mode
==========

The Nim compiler and most parts of the standard library support
a taint mode. Input strings are declared with the `TaintedString`:idx:
string type declared in the ``system`` module.

If the taint mode is turned on (via the ``--taintMode:on`` command line
option) it is a distinct string type which helps to detect input
validation errors:

.. code-block:: nim
echo "your name: "
var name: TaintedString = stdin.readline
# it is safe here to output the name without any input validation, so
# we simply convert `name` to string to make the compiler happy:
echo "hi, ", name.string
If the taint mode is turned off, ``TaintedString`` is simply an alias for
``string``.


Aliasing restrictions in parameter passing
==========================================

Expand Down
8 changes: 4 additions & 4 deletions lib/deprecated/pure/parseopt2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type
pos: int
remainingShortOptions: string
kind*: CmdLineKind ## the detected command line token
key*, val*: TaintedString ## key and value pair; ``key`` is the option
key*, val*: string ## key and value pair; ``key`` is the option
## or the argument, ``value`` is not "" if
## the option was given a value

Expand Down Expand Up @@ -80,7 +80,7 @@ proc nextOption(p: var OptParser, token: string, allowEmpty: bool) =
proc next(p: var OptParser) =
if p.remainingShortOptions.len != 0:
p.kind = cmdShortOption
p.key = TaintedString(p.remainingShortOptions[0..0])
p.key = p.remainingShortOptions[0..0]
p.val = ""
p.remainingShortOptions = p.remainingShortOptions[1..p.remainingShortOptions.len-1]
return
Expand All @@ -103,13 +103,13 @@ proc next(p: var OptParser) =
p.key = token
p.val = ""

proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo2$1".} =
proc cmdLineRest*(p: OptParser): string {.rtl, extern: "npo2$1".} =
## Returns the part of command line string that has not been parsed yet,
## properly quoted.
return p.cmd[p.pos..p.cmd.len-1].quoteShellCommand

type
GetoptResult* = tuple[kind: CmdLineKind, key, val: TaintedString]
GetoptResult* = tuple[kind: CmdLineKind, key, val: string]

iterator getopt*(p: var OptParser): GetoptResult =
## This is an convenience iterator for iterating over the given OptParser object.
Expand Down
22 changes: 11 additions & 11 deletions lib/impure/rdstdin.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
## echo userResponse

when defined(Windows):
proc readLineFromStdin*(prompt: string): TaintedString {.
proc readLineFromStdin*(prompt: string): string {.
tags: [ReadIOEffect, WriteIOEffect].} =
## Reads a line from stdin.
stdout.write(prompt)
result = readLine(stdin)

proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {.
proc readLineFromStdin*(prompt: string, line: var string): bool {.
tags: [ReadIOEffect, WriteIOEffect].} =
## Reads a `line` from stdin. `line` must not be
## ``nil``! May throw an IO exception.
Expand All @@ -40,35 +40,35 @@ when defined(Windows):
result = readLine(stdin, line)

elif defined(genode):
proc readLineFromStdin*(prompt: string): TaintedString {.
proc readLineFromStdin*(prompt: string): string {.
tags: [ReadIOEffect, WriteIOEffect].} =
stdin.readLine()

proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {.
proc readLineFromStdin*(prompt: string, line: var string): bool {.
tags: [ReadIOEffect, WriteIOEffect].} =
stdin.readLine(line)

else:
import linenoise

proc readLineFromStdin*(prompt: string): TaintedString {.
proc readLineFromStdin*(prompt: string): string {.
tags: [ReadIOEffect, WriteIOEffect].} =
var buffer = linenoise.readLine(prompt)
if isNil(buffer):
raise newException(IOError, "Linenoise returned nil")
result = TaintedString($buffer)
if result.string.len > 0:
result = $buffer
if result.len > 0:
historyAdd(buffer)
linenoise.free(buffer)

proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {.
proc readLineFromStdin*(prompt: string, line: var string): bool {.
tags: [ReadIOEffect, WriteIOEffect].} =
var buffer = linenoise.readLine(prompt)
if isNil(buffer):
line.string.setLen(0)
line.setLen(0)
return false
line = TaintedString($buffer)
if line.string.len > 0:
line = $buffer
if line.len > 0:
historyAdd(buffer)
linenoise.free(buffer)
result = true
18 changes: 9 additions & 9 deletions lib/pure/asyncftpclient.nim
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ type

const multiLineLimit = 10000

proc expectReply(ftp: AsyncFtpClient): Future[TaintedString] {.async.} =
proc expectReply(ftp: AsyncFtpClient): Future[string] {.async.} =
var line = await ftp.csock.recvLine()
result = TaintedString(line)
result = line
var count = 0
while line.len > 3 and line[3] == '-':
## Multi-line reply.
Expand All @@ -146,7 +146,7 @@ proc expectReply(ftp: AsyncFtpClient): Future[TaintedString] {.async.} =
if count >= multiLineLimit:
raise newException(ReplyError, "Reached maximum multi-line reply count.")

proc send*(ftp: AsyncFtpClient, m: string): Future[TaintedString] {.async.} =
proc send*(ftp: AsyncFtpClient, m: string): Future[string] {.async.} =
## Send a message to the server, and wait for a primary reply.
## ``\c\L`` is added for you.
##
Expand All @@ -158,9 +158,9 @@ proc send*(ftp: AsyncFtpClient, m: string): Future[TaintedString] {.async.} =
await ftp.csock.send(m & "\c\L")
return await ftp.expectReply()

proc assertReply(received: TaintedString, expected: varargs[string]) =
proc assertReply(received: string, expected: varargs[string]) =
for i in items(expected):
if received.string.startsWith(i): return
if received.startsWith(i): return
raise newException(ReplyError,
"Expected reply '$1' got: $2" %
[expected.join("' or '"), received.string])
Expand All @@ -169,7 +169,7 @@ proc pasv(ftp: AsyncFtpClient) {.async.} =
## Negotiate a data connection.
ftp.dsock = newAsyncSocket()

var pasvMsg = (await ftp.send("PASV")).string.strip.TaintedString
var pasvMsg = (await ftp.send("PASV")).strip
assertReply(pasvMsg, "227")
var betweenParens = captureBetween(pasvMsg.string, '(', ')')
var nums = betweenParens.split(',')
Expand Down Expand Up @@ -201,11 +201,11 @@ proc connect*(ftp: AsyncFtpClient) {.async.} =
if ftp.pass != "":
assertReply(await(ftp.send("PASS " & ftp.pass)), "230")

proc pwd*(ftp: AsyncFtpClient): Future[TaintedString] {.async.} =
proc pwd*(ftp: AsyncFtpClient): Future[string] {.async.} =
## Returns the current working directory.
let wd = await ftp.send("PWD")
assertReply wd, "257"
return wd.string.captureBetween('"').TaintedString # "
return wd.captureBetween('"') # "

proc cd*(ftp: AsyncFtpClient, dir: string) {.async.} =
## Changes the current directory on the remote FTP server to ``dir``.
Expand Down Expand Up @@ -253,7 +253,7 @@ proc createDir*(ftp: AsyncFtpClient, dir: string, recursive = false){.async.} =
if not recursive:
assertReply(await(ftp.send("MKD " & dir.normalizePathSep)), "257")
else:
var reply = TaintedString""
var reply = ""
var previousDirs = ""
for p in split(dir, {os.DirSep, os.AltSep}):
if p != "":
Expand Down
8 changes: 4 additions & 4 deletions lib/pure/cgi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,14 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string =
if methodNone notin allowedMethods:
cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")

iterator decodeData*(data: string): tuple[key, value: TaintedString] =
iterator decodeData*(data: string): tuple[key, value: string] =
## Reads and decodes CGI data and yields the (name, value) pairs the
## data consists of.
for (key, value) in uri.decodeQuery(data):
yield (key, value)

iterator decodeData*(allowedMethods: set[RequestMethod] =
{methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] =
{methodNone, methodPost, methodGet}): tuple[key, value: string] =
## Reads and decodes CGI data and yields the (name, value) pairs the
## data consists of. If the client does not use a method listed in the
## `allowedMethods` set, a `CgiError` exception is raised.
Expand Down Expand Up @@ -301,10 +301,10 @@ proc setCookie*(name, value: string) =
var
gcookies {.threadvar.}: StringTableRef

proc getCookie*(name: string): TaintedString =
proc getCookie*(name: string): string =
## Gets a cookie. If no cookie of `name` exists, "" is returned.
if gcookies == nil: gcookies = parseCookies(getHttpCookie())
result = TaintedString(gcookies.getOrDefault(name))
result = gcookies.getOrDefault(name)

proc existsCookie*(name: string): bool =
## Checks if a cookie of `name` exists.
Expand Down
18 changes: 9 additions & 9 deletions lib/pure/includes/osenv.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ when not declared(os) and not declared(ospaths):
{.error: "This is an include file for os.nim!".}

when defined(nodejs):
proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} =
proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} =
var ret: cstring
let key2 = key.cstring
{.emit: "`ret` = process.env[`key2`];".}
Expand All @@ -25,7 +25,7 @@ when defined(nodejs):
var key2 = key.cstring
{.emit: "delete process.env[`key2`];".}

iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
var num: int
var keys: RootObj
{.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".}
Expand Down Expand Up @@ -144,7 +144,7 @@ else:
if startsWith(environment[i], temp): return i
return -1

proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} =
proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} =
## Returns the value of the `environment variable`:idx: named `key`.
##
## If the variable does not exist, `""` is returned. To distinguish
Expand All @@ -165,11 +165,11 @@ else:
else:
var i = findEnvVar(key)
if i >= 0:
return TaintedString(substr(environment[i], find(environment[i], '=')+1))
return substr(environment[i], find(environment[i], '=')+1)
else:
var env = c_getenv(key)
if env == nil: return TaintedString(default)
result = TaintedString($env)
if env == nil: return default
result = $env

proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
## Checks whether the environment variable named `key` exists.
Expand Down Expand Up @@ -248,7 +248,7 @@ else:
raiseOSError(osLastError())
environment.delete(indx)

iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
## Iterate over all `environments variables`:idx:.
##
## In the first component of the tuple is the name of the current variable stored,
Expand All @@ -262,5 +262,5 @@ else:
getEnvVarsC()
for i in 0..high(environment):
var p = find(environment[i], '=')
yield (TaintedString(substr(environment[i], 0, p-1)),
TaintedString(substr(environment[i], p+1)))
yield (substr(environment[i], 0, p-1),
substr(environment[i], p+1))
12 changes: 6 additions & 6 deletions lib/pure/memfiles.nim
Original file line number Diff line number Diff line change
Expand Up @@ -428,17 +428,17 @@ iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline
ms.data = cast[pointer](cast[int](ending) +% 1) # skip delim
remaining = mfile.size - (ms.data -! mfile.mem)

iterator lines*(mfile: MemFile, buf: var TaintedString, delim = '\l',
eat = '\r'): TaintedString {.inline.} =
iterator lines*(mfile: MemFile, buf: var string, delim = '\l',
eat = '\r'): string {.inline.} =
## Replace contents of passed buffer with each new line, like
## `readLine(File) <io.html#readLine,File,TaintedString>`_.
## `readLine(File) <io.html#readLine,File,string>`_.
## `delim`, `eat`, and delimiting logic is exactly as for `memSlices
## <#memSlices.i,MemFile,char,char>`_, but Nim strings are returned.
##
## Example:
##
## .. code-block:: nim
## var buffer: TaintedString = ""
## var buffer: string = ""
## for line in lines(memfiles.open("foo"), buffer):
## echo line

Expand All @@ -448,7 +448,7 @@ iterator lines*(mfile: MemFile, buf: var TaintedString, delim = '\l',
copyMem(addr string(buf)[0], ms.data, ms.size)
yield buf

iterator lines*(mfile: MemFile, delim = '\l', eat = '\r'): TaintedString {.inline.} =
iterator lines*(mfile: MemFile, delim = '\l', eat = '\r'): string {.inline.} =
## Return each line in a file as a Nim string, like
## `lines(File) <io.html#lines.i,File>`_.
## `delim`, `eat`, and delimiting logic is exactly as for `memSlices
Expand All @@ -460,7 +460,7 @@ iterator lines*(mfile: MemFile, delim = '\l', eat = '\r'): TaintedString {.inlin
## for line in lines(memfiles.open("foo")):
## echo line

var buf = TaintedString(newStringOfCap(80))
var buf = newStringOfCap(80)
for line in lines(mfile, buf, delim, eat):
yield buf

Expand Down
Loading

0 comments on commit 78a9958

Please sign in to comment.