10000 Added startup option `--javascript.user-defined-functions`. by jsteemann · Pull Request #18048 · arangodb/arangodb · GitHub
[go: up one dir, main page]

Skip to content

Added startup option --javascript.user-defined-functions. #18048

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
devel
-----

* Added startup option `--javascript.user-defined-functions`.
This option controls whether JavaScript user-defined functions (UDFs) can
be used in AQL queries. The option defaults to `true`. The option can be
set to `false` to disallow using JavaScript UDFs from inside AQL queries.
In that case, a parse error will be thrown when trying to run a query that
invokes a UDF.

* Updated transitive JS dependency hoek to @hapi/hoek@8.5.1 to resolve
CVE-2020-36604 in joi.

* Updated JS dependency minimatch to 3.1.2 to resolve CVE-2022-3517.

* Updated JS dependency qs to 6.11.0 to resolve CVE-2022-24999.

* Deleted customizable Pregel (AIR) and Greenspun library

* Allowing enabling/disabling supervision maintenance mode also via followers
in active failover mode. Previously the supervision maintenance mode could
only be enabled/disabled by making a call to the active failover leader.
Expand Down
17 changes: 16 additions & 1 deletion arangod/Aql/Ast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include "Utilities/NameValidator.h"
#include "VocBase/LogicalCollection.h"
#include "VocBase/LogicalView.h"
#include "V8Server/V8DealerFeature.h"

#include <absl/strings/str_cat.h>

Expand Down Expand Up @@ -1884,12 +1885,26 @@ AstNode* Ast::createNodeFunctionCall(std::string_view functionName,
_functionsMayAccessDocuments = true;
}
} else {
// user-defined function
// user-defined function (UDF)
if (_query.vocbase().server().hasFeature<V8DealerFeature>() &&
!_query.vocbase()
.server()
.getFeature<V8DealerFeature>()
.allowJavaScriptUdfs()) {
// usage of user-defined functions is disallowed
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_PARSE,
"usage of AQL user-defined functions "
"(UDFs) is disallowed via configuration");
}

node = createNode(NODE_TYPE_FCALL_USER);
// register the function name
char* fname = _resources.registerString(normalized);
node->setStringValue(fname, normalized.size());

// a JavaScript user-defined function can potentially read documents
// via JavaScript document or AQL APIs. we don't know for sure, so we
// need to assume the worst case here.
_functionsMayAccessDocuments = true;
}

Expand Down
14 changes: 13 additions & 1 deletion arangod/V8Server/V8DealerFeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,14 @@ V8DealerFeature::V8DealerFeature(Server& server)
_gcFrequency(60.0),
_gcInterval(2000),
_maxContextAge(60.0),
_copyInstallation(false),
_nrMaxContexts(0),
_nrMinContexts(0),
_nrInflightContexts(0),
_maxContextInvocations(0),
_copyInstallation(false),
_allowAdminExecute(false),
_allowJavaScriptTransactions(true),
_allowJavaScriptUdfs(true),
_allowJavaScriptTasks(true),
_enableJS(true),
_nextId(0),
Expand Down Expand Up @@ -328,6 +329,17 @@ or teardown commands for execution on the server.)");
arangodb::options::Flags::OnSingle))
.setIntroducedIn(30800);

options
->addOption(
"--javascript.user-defined-functions",
"Enable JavaScript user-defined functions (UDFs) in AQL queries.",
new BooleanParameter(&_allowJavaScriptUdfs),
arangodb::options::makeFlags(
arangodb::options::Flags::DefaultNoComponents,
arangodb::options::Flags::OnCoordinator,
arangodb::options::Flags::OnSingle))
.setIntroducedIn(31004);

options
->addOption("--javascript.tasks", "Enable JavaScript tasks.",
new BooleanParameter(&_allowJavaScriptTasks),
Expand Down
27 changes: 20 additions & 7 deletions arangod/V8Server/V8DealerFeature.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,35 @@ class V8DealerFeature final : public ArangodFeature {
std::string _startupDirectory;
std::string _nodeModulesDirectory;
std::vector<std::string> _moduleDirectories;
// maximum number of contexts to create
uint64_t _nrMaxContexts;
// minimum number of contexts to keep
uint64_t _nrMinContexts;
// number of contexts currently in creation
uint64_t _nrInflightContexts;
// maximum number of V8 context invocations
uint64_t _maxContextInvocations;

// copy JavaScript files into database directory on startup
bool _copyInstallation;
uint64_t _nrMaxContexts; // maximum number of contexts to create
uint64_t _nrMinContexts; // minimum number of contexts to keep
uint64_t _nrInflightContexts; // number of contexts currently in creation
uint64_t _maxContextInvocations; // maximum number of V8 context invocations
// enable /_admin/execute API
bool _allowAdminExecute;
// allow JavaScript transactions?
bool _allowJavaScriptTransactions;
// allow JavaScript user-defined functions?
bool _allowJavaScriptUdfs;
// allow JavaScript tasks (tasks module)?
bool _allowJavaScriptTasks;
// enable JavaScript globally
bool _enableJS;

public:
bool allowAdminExecute() const { return _allowAdminExecute; }
bool allowJavaScriptTransactions() const {
bool allowAdminExecute() const noexcept { return _allowAdminExecute; }
bool allowJavaScriptTransactions() const noexcept {
return _allowJavaScriptTransactions;
}
bool allowJavaScriptTasks() const { return _allowJavaScriptTasks; }
bool allowJavaScriptUdfs() const noexcept { return _allowJavaScriptUdfs; }
bool allowJavaScriptTasks() const noexcept { return _allowJavaScriptTasks; }

bool addGlobalContextMethod(std::string const&);
void collectGarbage();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*jshint globalstrict:false, strict:false */
/*global assertEqual, getOptions, fail */

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2023, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

if (getOptions === true) {
return {
'javascript.user-defined-functions': 'false',
};
}

const jsunity = require("jsunity");
const arangodb = require("@arangodb");
const udf = require("@arangodb/aql/functions");
const db = arangodb.db;
const ERRORS = arangodb.errors;

function UserDefinedFunctionsTestSuite () {
return {

setUpAll : function () {
udf.register("some::func", function() { return 1; });
},

tearDownAll : function () {
udf.unregister("some::func");
},

testUseUDF : function () {
try {
// invoking a UDF should fail with a parse error
db._query("RETURN some::func()");
fail();
} catch (err) {
assertEqual(err.errorNum, ERRORS.ERROR_QUERY_PARSE.code);
}
},

testNormalAqlFunction : function () {
// invoking normal AQL functions should just work
let res = db._query("RETURN MAX([1, 2, 3])");
assertEqual([ 3 ], res.toArray());
},
};
}

jsunity.run(UserDefinedFunctionsTestSuite);

return jsunity.done();
0