A Taste of Monte: Hello Web

Let’s see what a simple web server looks like in Monte:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import "lib/http/server" =~ [=> makeHTTPEndpoint]
exports (main)

def helloWeb(_request) as DeepFrozen:
    "Build a simple HTML response."

    return [200, ["Content-Type" => "text/html"], b`<p>Hello!</p>`]

def main(argv, => makeTCP4ServerEndpoint) :Int as DeepFrozen:
    "Obtain a port and create an HTTP server on that port."

    def portNum :Int := _makeInt(argv.last())
    def ep := makeHTTPEndpoint(makeTCP4ServerEndpoint(portNum))
    traceln(`serving on port $portNum`)
    ep.listen(helloWeb)
    return 0

The makeHTTPEndpoint import reads much like Python’s from http.server import makeHTTPEndpoint, though the mechanics of a module declaration in monte are a bit different: it uses pattern matching to bind names to objects imported from modules.

We declare that this module exports its main function, as is conventional for executable programs.

Todo

Document how to compile and run such a script.

Blocks in Monte are typically written with indentation, like Python, though blocks in general may be written with curly-braces as well.

Note

Tabs are a syntax error in Monte.

Expressions

The def-expr for defining the helloWeb function is similar to Python’s syntax for defining functions.

The expression inside the call to traceln(…) does string interpolation, similar to Perl, Ruby, and bash. It is a quasiliteral expression:

> def portNum := 8080
> `serving on port $portNum`
"serving on port 8080"

Another quasiliteral is b`<p>Hello!</p>`, which denotes a Bytes object rather than a character string.

Objects and Message Passing

Monte is a pure object language, which means that all values in Monte are objects. All operations on objects are done by passing messages. This includes ordinary method calls like argv.last() as well as function calls such as traceln(portNum) and even syntax for constructing lists like [200, [], body] and maps like ["C" => "t"].

Cooperation Without Vulerability

Suppose our server takes an arbitrary expression from the web client and evaluates it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import "lib/http/server" =~ [=> makeHTTPEndpoint]
import "lib/http/tag" =~ [=> tag]
import "formData" =~ [=> fieldMap]
exports (main)

object calculator as DeepFrozen:
    to run(request):
        return switch (request.getVerb()):
            match =="GET":
                calculator.get(request)
            match =="POST":
                calculator.post(request)

    to get(_request):
        def body := b`
        <form method="POST">
          <label>Arbitrary code to execute:<input name="code" /></label>
        </form>
        `
        return [200, ["Content-Type" => "text/html"], body]

    to post(request):
        def code := fieldMap(request.getBody())["code"]
        def result := eval(code, safeScope)
        # NB: The `tag` object does automatic HTML escaping. No extra effort
        # is required to prevent XSS. ~ C.
        def html := tag.pre(M.toString(result))
        return [200, ["Content-Type" => "text/plain"], b`$html`]

def main(argv, => makeTCP4ServerEndpoint) :Int as DeepFrozen:
    def portNum := _makeInt(argv.last())
    def ep := makeHTTPEndpoint(makeTCP4ServerEndpoint(portNum))
    traceln(`serving $calculator on port $portNum`)
    ep.listen(calculator)
    return 0

With conventional languages and frameworks, this would be injection, #1 on the list of top 10 web application security flaws:

Injection can result in data loss or corruption, lack of accountability, or denial of access. Injection can sometimes lead to complete host takeover.

But using object capability discipline, untrusted code has only the authority that we explicitly give it. This rich form of cooperation comes with dramatically less vulnerability [1]. The environment in this example is safeScope, which is the same environment modules are evaluated in – it provides basic runtime services such as constructors for lists, maps, and other structures, but no “powerful” objects. In particular, makeTCP4ServerEndpoint is not in scope when the remote code is executed, so the code cannot use it to access the network. Neither does the code have any access to read from nor write to files, clobber global state, nor launch missiles.

Notes

[1]We implicitly grant authority to compute indefinitely. Object capability discipline does not address denial of service. Monte’s vats include a conventional mechanism to put a finite limit on computation.