ArangoDB v4.x is under development and not released yet.
This documentation is not final and potentially incomplete.
arangosh Details
The behavior and configuration of the ArangoDB Shell
Interaction
You can paste multiple lines into arangosh, given the first line ends with an opening brace:
for (var i = 0; i < 10; i ++) {
require("@arangodb").print("Hello world " + i + "!\n");
}Show output
Hello world 0!
Hello world 1!
Hello world 2!
Hello world 3!
Hello world 4!
Hello world 5!
Hello world 6!
Hello world 7!
Hello world 8!
Hello world 9!To load your own JavaScript code into the current JavaScript interpreter context, use the load command:
require("internal").load("/tmp/test.js")You can exit arangosh using the key combination Ctrl + D
or by
typing quit and hitting Return
.
Shell Output
The ArangoDB shell prints the output of the last evaluated expression by default:
42 * 23Show output
966In order to prevent printing the result of the last evaluated expression, the expression result can be captured in a variable, e.g.
var calculationResult = 42 * 23Show output
Empty OutputThere is also the print function to explicitly print out values in the
ArangoDB shell:
print({ a: "123", b: [1,2,3], c: "test" });Show output
{
"a" : "123",
"b" : [
1,
2,
3
],
"c" : "test"
}By default, the ArangoDB shell uses a pretty printer when JSON documents are printed. This ensures documents are printed in a human-readable way:
db._create("five")
for (var i = 0; i < 5; i++) {
db.five.save({value:i});
}
db.five.toArray()Show output
[ArangoCollection 67023, "five" (type document, status loaded)]
{
"_id" : "five/67040",
"_key" : "67040",
"_rev" : "_laD07EW--B"
}
[
{
"_key" : "67032",
"_id" : "five/67032",
"_rev" : "_laD07ES---",
"value" : 0
},
{
"_key" : "67034",
"_id" : "five/67034",
"_rev" : "_laD07EW---",
"value" : 1
},
{
"_key" : "67036",
"_id" : "five/67036",
"_rev" : "_laD07EW--_",
"value" : 2
},
{
"_key" : "67038",
"_id" : "five/67038",
"_rev" : "_laD07EW--A",
"value" : 3
},
{
"_key" : "67040",
"_id" : "five/67040",
"_rev" : "_laD07EW--B",
"value" : 4
}
]While the pretty-printer produces nice looking results, it needs a lot of
screen space for each document. Sometimes a more dense output might be better.
In this case, the pretty printer can be turned off using the command
stop_pretty_print().
To turn on pretty printing again, use the start_pretty_print() command.
Escaping
In AQL, escaping is done traditionally with the backslash character: \.
For literal backslashes, you need to double backslashes to \\.
arangosh requires another level of escaping, also with the backslash character.
It adds up to four backslashes that need to be written in arangosh for a single
literal backslash (c:\tmp\test.js):
db._query('RETURN "c:\\\\tmp\\\\test.js"')You can use bind variables to mitigate this:
var somepath = "c:\\tmp\\test.js"
db._query(aql`RETURN ${somepath}`)Database Wrappers
arangosh provides the db object
by default, and this object can be used for switching to a different database
and managing collections inside the current database.
For a list of available methods for the db object, type
db._help();
Show output
--------------------------- ArangoDatabase (db) help ---------------------------
Administration Functions:
_help() this help
_flushCache() flush and refill collection cache
Collection Functions:
_collections() list all collections
_collection(<name>) get collection by identifier/name
_create(<name>, <properties>) creates a new collection
_createEdgeCollection(<name>) creates a new edge collection
_drop(<name>) delete a collection
Document Functions:
_document(<id>) get document by handle (_id)
_replace(<id>, <data>, <overwrite>) overwrite document
_update(<id>, <data>, <overwrite>, partially update document
<keepNull>)
_remove(<id>) delete document
_exists(<id>) checks whether a document exists
_truncate() delete all documents
Database Management Functions:
_createDatabase(<name>) creates a new database
_dropDatabase(<name>) drops an existing database
_useDatabase(<name>) switches into an existing database
_drop(<name>) delete a collection
_name() name of the current database
Query / Transaction Functions:
_executeTransaction(<transaction>) execute transaction
_query(<query>) execute AQL query
_createStatement(<data>) create and return AQL query
View Functions:
_views() list all views
_view(<name>) get view by name
_createView(<name>, <type>, creates a new view
<properties>)
_dropView(<name>) delete a view
License Functions:
_getLicense() get license information
_setLicense(<license-string>) set license string
The implementation of the db object wraps HTTP requests
to ArangoDB’s HTTP API.
It means that the following code performs around 100k HTTP requests:
for (var i = 0; i < 100000; i++) {
db.test.save({ name: { first: "Jan" }, count: i});
}You should avoid making excessive calls like this and instead save batches of documents in fewer HTTP requests:
var batch = [];
for (var i = 0; i < 100000; i++) {
batch.push({ name: { first: "Jan" }, count: i});
if (batch.length >= 1000) {
db.test.save(batch);
batch = [];
}
}
if (batch.length > 0) {
db.test.save(batch);
}Using arangosh via Unix shebang mechanisms
In Unix operating systems, you can start scripts by specifying the interpreter in the first line of the script.
This is commonly called shebang or hash bang. You can also do that with arangosh, i.e. create ~/test.js:
#!/usr/bin/arangosh --javascript.execute
require("internal").print("hello world")
db._query("FOR x IN test RETURN x").toArray()
Note that the first line has to end with a blank in order to make it work. Mark it executable to the OS:
> chmod a+x ~/test.js
and finally try it out:
> ~/test.js
Shell Configuration
arangosh looks for a user-defined startup script named .arangosh.rc in the
user’s home directory on startup. The home directory is likely at /home/<username>/
on Unix/Linux.
If the file .arangosh.rc is present in the home directory, arangosh executes
the contents of this file inside the global scope.
You can use this to define your own extra variables and functions that you need often.
For example, you could put the following into the .arangosh.rc file in your home
directory:
// "var" keyword avoided intentionally...
// otherwise "timed" would not survive the scope of this script
global.timed = function (cb) {
console.time("callback");
cb();
console.timeEnd("callback");
};This makes a function named timed available in arangosh in the global scope.
You can now start arangosh and invoke the function like this:
timed(function () {
for (var i = 0; i < 1000; ++i) {
db.test.save({ value: i });
}
});Please keep in mind that, if present, the .arangosh.rc file needs to contain valid
JavaScript code. If you want any variables in the global scope to survive you need to
omit the var keyword for them. Otherwise, the variables are only visible inside
the script itself, but not outside.
JavaScript security options
The arangosh shell has several options that allow you to limit what the JavaScript code can access. Below you find an overview of the relevant options.
Allowlists and denylists
Several options exist to restrict the functionality of JavaScript code to just certain allowed subsets. Which subset of functionality is available can be controlled via “denylisting” and “allowlisting” access to individual components.
The set theory for these lists works as follow:
- No allowlist or denylist is specified:
Everything is allowed. - Only a denylist is specified:
Everything is allowed except a set of items matching the denylist. - Only an allowlist is specified:
Everything is disallowed except the set of items matching the allowlist. - Both allowlist and denylist are specified:
Everything is disallowed except the set of items matching the allowlist. From this allowed set, subsets can be forbidden again using the denylist.
Values for denylist and allowlist options need to be specified as ECMAScript regular expressions.
A pattern matches if it is found anywhere in a value, so it can match more than you intend. To restrict a pattern to exact matches:
- Anchor it with
^(...)to only match the prefix, or^(...)$to match the whole value. - Escape characters with a special meaning, such as the dot: use
\.for a literal dot, which otherwise matches any character.
For example, use ^http://example\.com/ instead of example.com for validating
request URLs and ^/etc/issue$ instead of /etc/issue for specific file paths.
Each option can be used multiple times. When specifying more than one pattern, these patterns are combined with a logical or to the actual pattern arangosh uses.
These patterns and how they are applied can be observed in the arangosh log
output by enabling --log.level V8=debug.
Options for allowlisting and denylisting
The following options are available for allowlisting and denylisting access to dedicated functionality for application code:
--javascript.startup-options-[allowlist|denylist]:
These options control which startup options are exposed to JavaScript code.--javascript.environment-variables-[allowlist|denylist]:
These options control which environment variables are exposed to JavaScript code.--javascript.files-[allowlist|denylist]:
These options control which filesystem paths can be accessed from JavaScript code.--javascript.endpoints-[allowlist|denylist]:
These options control which endpoints can be used from within the@arangodb/requestJavaScript module.
Startup option access
Access to startup option settings can be restricted for JavaScript code to hide sensitive information that may be present in the values of the options.
The regular expression matching is case-sensitive.
Example:
--javascript.startup-options-allowlist "^server\."
--javascript.startup-options-allowlist "^log\."
--javascript.startup-options-denylist "^javascript\."
--javascript.startup-options-denylist "^endpoint$"
These sets are resolved internally to the following regular expressions:
--javascript.startup-options-allowlist = "^server\.|^log\."
--javascript.startup-options-denylist = "^javascript\.|^endpoint$"
Invoking arangosh with these options hides the denied command-line options from the output of the following method:
require('internal').options()An exception is thrown when trying to access items that are masked in the same way as if they wouldn’t exist.
Environment variable access
Access to environment variables can be restricted for JavaScript code to hide sensitive information that may be stored in environment variables.
The regular expression matching is case-sensitive.
Example:
--javascript.environment-variables-allowlist "^ARANGO_"
--javascript.environment-variables-denylist "PASSWORD"
This allows JavaScript code to only see environment variables that start
with ARANGO_, except if they contain PASSWORD. It excludes the variables
PATH and ARANGO_ROOT_PASSWORD for instance.
PASSWORD won’t exclude environment variables that include password. You may
use [Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd] for case-insensitive matching.
You can test the allow-/denylisting in arangosh, here using the ArangoDB 4.0 container image:
docker run --rm -e ARANGO_ROOT_PASSWORD="secret" arangodb:4.0 \
arangosh --javascript.execute-string "print(process.env)"
{
"HOSTNAME" : "0aea68ec522d",
"SHLVL" : "1",
"HOME" : "/root",
"ARANGO_ROOT_PASSWORD" : "secret",
"PATH" : "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"PWD" : "/",
"ICU_DATA_LEGACY" : "/usr/share/arangodb3/",
"ICU_DATA" : "/usr/share/arangodb3/"
}docker run --rm -e ARANGO_ROOT_PASSWORD="secret" arangodb:4.0 \
arangosh --javascript.execute-string "print(process.env)" \
--javascript.environment-variables-allowlist "^(ARANGO|ICU)_" \
--javascript.environment-variables-denylist "PASSWORD"
...
[Object {
"ICU_DATA_LEGACY" : "/usr/share/arangodb3/",
"ICU_DATA" : "/usr/share/arangodb3/"
}]File access
Access to directories and files from JavaScript operations can be restricted
using an allowlist via the --javascript.files-allowlist startup option and a
denylist via the --javascript.files-denylist startup option. Any files or
directories not matching the allowlist are inaccessible from JavaScript
filesystem functions, and the denylist can forbid subsets of the allowed paths
again.
The regular expression matching is case-insensitive.
Example:
--javascript.files-allowlist "^/etc/required/"
--javascript.files-allowlist "^/etc/mtab/"
--javascript.files-allowlist "^/etc/issue$"
--javascript.files-denylist "^/etc/required/secrets/"
The file /etc/issue can be accessed and all files in the directories
/etc/required and /etc/mtab plus their subdirectories are accessible,
except the files in /etc/required/secrets which are denied again. Access to
files in any other directories is disallowed from JavaScript operations, with
the following exceptions:
Temporary directory:
JavaScript code is given access to this directory for storing temporary files. The temporary directory location can be specified explicitly via the--temp.pathstartup option. If the option is not specified, arangosh automatically use a subdirectory of the system’s temporary directory.Bundled JavaScript code, shipped with arangosh:
Files in this directory and its subdirectories are readable for JavaScript code running in arangosh. The exact path can be specified with the--javascript.startup-directorystartup option.
URL access
Access to external HTTP resources can be restricted for JavaScript code.
Filtering is done against the full request URL as used in the JavaScript code,
including the protocol, hostname/IP address, port, path, query parameters, and
fragment identifier (e.g. http://host:port/path/?query=1#anchor).
The regular expression matching is case-insensitive.
Example:
--javascript.endpoints-allowlist "https?://(www\.)?arangodb\.org(:80|443)?/"
--javascript.endpoints-denylist "https?://(www\.)?arangodb\.org(:80|443)?/admin"
This allows requests to arangodb.org and www.arangodb.org over the HTTP and
HTTPS protocols, using any paths (and query parameters) except paths with the
prefix /admin.
Keep in mind that these startup options are treated as regular expressions.
Certain characters have special meaning (e.g. .) that may require escaping
(\.) and the expression only needs to match a substring
(anywhere in the request URL) by default.
It is recommended to fully specify URLs and to use a leading ^ and potentially
a trailing $ to ensure that no other than the intended URLs are matched.
To allow requests via HTTP and HTTPS to the host arangodb.org, you should
use ^https?://arangodb\.org(:80|:443)?/ as the regular expression for the
--javascript.endpoints-allowlist startup option. It only matches the intended
URLs, that is any request path with arangodb.org as the host, with or without
explicit mention of the HTTP/HTTPS default port, and not accidentally matching
subdomains of third-parties (like http://arangodb.org.evil.domain) due to the
slash after the domain/port:
http://arangodb.org/http://arangodb.org/folder/file.htmlhttp://arangodb.org/api?query=1&arg=2http://arangodb.org:80/http://arangodb.org:80/folder/file.htmlhttp://arangodb.org:80/api?query=1&arg=2https://arangodb.org/https://arangodb.org/folder/file.htmlhttps://arangodb.org/api?query=1&arg=2https://arangodb.org:443/https://arangodb.org:443/folder/file.htmlhttps://arangodb.org:443/api?query=1&arg=2- etc.
You should never use something like arangodb.org as regular expression for the
allowlist because it matches any request URL that contains arangodb followed
by any character and org anywhere in the URL, including the path and
query parameters. This most likely allows access to unintended URLs:
http://arangodb.org/✅http://arangodb.org/folder/file.html✅https://arangodb.org/✅https://arangodb.org:12345/❓ftp://arangodb.org❓https://subdomain.arangodb.organic/⚠️https://arangodb-org.evil.domain/⚠️https://evil.domain/path/?query=arangodb+org⚠️- etc.
An unescaped . represents any character. For a literal dot, use \..
You can restrict the access to specific protocols, even though http:// and
https:// are the only ones supported by the request module.
Specifying http://arangodb\.org still matches more URLs than intended, namely
any request URL that contains http://arangodb.org anywhere:
http://arangodb.org/✅http://arangodb.org:12345/❓http://arangodb.organic/⚠️http://arangodb.org.evil.domain/⚠️http://evil.domain/path/?query=http://arangodb.org⚠️- etc.
You should anchor regular expressions for the allowlist to the beginning of the
request URL with a leading ^. You can also anchor it to the end with a trailing
$, but this may limit it too much.
Specifying ^http://arangodb\.org$ doesn’t match any request URL because the
path in an HTTP request cannot be empty, and the regular expression doesn’t
match http://arangodb.org/ (with / as the path).
Specifying ^http://arangodb\.org/$ only matches http://arangodb.org/, that is
the root path. You are able to access the homepage for this host, but no other
pages like http://arangodb.org/folder/file.html. Despite port 80 being the
default HTTP port, it also doesn’t match requests to http://arangodb.org:80/
with an explicitly stated port. Conversely, specifying ^http://arangodb\.org:80/$
matches http://arangodb.org:80/ with an explicit port in the request URL but not
http://arangodb.org/ with the port left out.
You can make the port optional like ^http://arangodb\.org(:80)?/$.
However, the trailing $ restricts it to the root path and there cannot be any
query parameters in the URL. This means http://arangodb.org/folder/file.html
and http://arangodb.org/?query=1&arg=2 don’t match. To safely allow any path
using arangodb.org as the host, you can use ^http://arangodb\.org(:80)?/.
Note that there is no trailing $ for anchoring to the end of the request URL.
However, a slash is required after the hostname (arangodb.org) respectively
after the port (:80). Don’t forget the / as this prevents URLs like
http://arangodb.org.evil.domain from accidentally matching the regular expression.
You may restrict access to specific subpaths with a regular expression like
^http://arangodb\.org(:80)?/(folder/|api). It matches request URLs like
http://arangodb.org/folder/file.html and http://arangodb.org:80/api?query=1&arg=2
but not http://arangodb.org/foldError/ or http://arangodb.org:80/admin/api.
You can test the allow-/denylisting in arangosh as follows:
arangosh --javascript.endpoints-allowlist "^https://arangodb\.org(:443)?/"
127.0.0.1:8529@_system> require('internal').download('http://arangodb.org/file.zip')
JavaScript exception: ArangoError 11: not allowed to connect to this URL: http://arangodb.org/file.zip
...
127.0.0.1:8529@_system> require('internal').download('https://arangodb.org/file.zip')
<request permitted by allowlist>
\$, \\) unless the entire string is wrapped in single quotes
('http://arangodb\.org$' instead of http://arangodb\\.org\$).Additional JavaScript security options
In addition to the allowlisting and denylisting security options, the following extra options are available for locking down JavaScript access to certain functionality:
--javascript.allow-port-testing: If set totrue, this option enables thetestPortJavaScript function in theinternalmodule. The default value isfalse.--javascript.allow-external-process-control: If set totrue, this option allows the execution and control of external processes from JavaScript code via functions from theinternalmodule:executeExternalexecuteExternalAndWaitgetExternalSpawnedkillExternalsuspendExternalcontinueExternalstatusExternal
--javascript.harden: If set totrue, this setting deactivates the following JavaScript functions from theinternalmodule, which may leak information about the environment:getPid()logLevel()
The default value is
false.
