Skip to content

Commit

Permalink
Add propagation string case functions and Array.join function (#98)
Browse files Browse the repository at this point in the history
* String case operations

* Array join operator

* Fix lint

* Fix cpp-lint

* Fix cpp-lint

* Additional tests for Array.join

* Fix test denomination

* Fix arguments count

* Extract variables

* Proper initialization of newRanges for array join

* Remove unnecessary conditional

* Additional test for String case

* Fix lint

* Fix to avoid SegFault when checking new ranges

* Fix lint
  • Loading branch information
CarlesDD authored Mar 26, 2024
1 parent 7643f11 commit 288909f
Show file tree
Hide file tree
Showing 10 changed files with 771 additions and 1 deletion.
2 changes: 2 additions & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"./src/api/substring.cc",
"./src/api/replace.cc",
"./src/api/metrics.cc",
"./src/api/string_case.cc",
"./src/api/array_join.cc",
"./src/iast.cc"
],
"include_dirs" : [
Expand Down
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,7 @@ declare module 'datadog-iast-taint-tracking' {
substring(transactionId: string, subject: string, result: string, start: number, end: number): string;
substr(transactionId: string, subject: string, result: string, start: number, length: number): string;
replace(transactionId: string, result: string, thisArg: string, matcher: unknown, replacer: unknown): string;
stringCase(transactionId: string, result: string, thisArg: string): string;
arrayJoin(transactionId: string, result: string, thisArg: any[], separator?: any): string;
}
}
10 changes: 9 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ try {
},
substr (transaction, result) {
return result
},
stringCase (transaction, result) {
return result
},
arrayJoin (transaction, result) {
return result
}
}
}
Expand All @@ -75,7 +81,9 @@ const iastNativeMethods = {
trimEnd: addon.trimEnd,
slice: addon.slice,
substring: addon.substring,
substr: addon.substr
substr: addon.substr,
stringCase: addon.stringCase,
arrayJoin: addon.arrayJoin
}

module.exports = iastNativeMethods
145 changes: 145 additions & 0 deletions src/api/array_join.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/**
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
* This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
**/
#include <new>
#include <vector>
#include <memory>
#include <string>

#include "array_join.h"
#include "../tainted/range.h"
#include "../tainted/string_resource.h"
#include "../tainted/transaction.h"
#include "../iast.h"

using v8::FunctionCallbackInfo;
using v8::Value;
using v8::Local;
using v8::Isolate;
using v8::Object;
using v8::String;

using iast::tainted::Range;

namespace iast {
namespace api {

const int DEFAULT_JOIN_SEPARATOR_LENGTH = 1;

void copyRangesWithOffset(Transaction* transaction,
SharedRanges* origRanges,
SharedRanges** destRanges,
int offset) {
if (origRanges != nullptr) {
auto end = origRanges->end();
for (auto it = origRanges->begin(); it != end; it++) {
auto origRange = *it;
auto newRange = transaction->GetRange(
origRange->start + offset,
origRange->end + offset,
origRange->inputInfo,
origRange->secureMarks);

if (newRange != nullptr) {
if (*destRanges == nullptr) {
*destRanges = transaction->GetSharedVectorRange();
}
(*destRanges)->PushBack(newRange);
} else {
break;
}
}
}
}

SharedRanges* getJoinResultRanges(Isolate* isolate,
Transaction* transaction, v8::Array* arr,
SharedRanges* separatorRanges,
int separatorLength) {
auto length = arr->Length();
int offset = 0;
SharedRanges* newRanges = nullptr;
auto context = isolate->GetCurrentContext();
for (uint32_t i = 0; i < length; i++) {
if (i > 0) {
copyRangesWithOffset(transaction, separatorRanges, &newRanges, offset);
offset += separatorLength;
}
auto maybeItem = arr->Get(context, i);
if (!maybeItem.IsEmpty()) {
auto item = maybeItem.ToLocalChecked();
auto taintedItem = transaction->FindTaintedObject(utils::GetLocalPointer(item));
auto itemRanges = taintedItem ? taintedItem->getRanges() : nullptr;
copyRangesWithOffset(transaction, itemRanges, &newRanges, offset);
offset += utils::GetLength(isolate, item);
}
}

return newRanges;
}

void ArrayJoinOperator(const FunctionCallbackInfo<Value>& args) {
auto isolate = args.GetIsolate();

if (args.Length() < 3) {
isolate->ThrowException(v8::Exception::TypeError(
v8::String::NewFromUtf8(isolate,
"Wrong number of arguments",
v8::NewStringType::kNormal).ToLocalChecked()));
return;
}

auto result = args[1];

if (!result->IsString()) {
args.GetReturnValue().Set(result);
return;
}

auto transaction = GetTransaction(utils::GetLocalPointer(args[0]));
if (transaction == nullptr) {
args.GetReturnValue().Set(result);
return;
}

auto thisArg = args[2];
if (thisArg->IsObject()) {
auto arrObj = v8::Object::Cast(*thisArg);
if (arrObj->IsArray()) {
try {
int separatorLength = DEFAULT_JOIN_SEPARATOR_LENGTH;
SharedRanges* separatorRanges = nullptr;
if (args.Length() > 3) {
auto separatorArg = args[3];
auto separatorValue = (*separatorArg);
if (!separatorValue->IsUndefined()) {
auto taintedSeparator = transaction->FindTaintedObject(utils::GetLocalPointer(separatorArg));
separatorRanges = taintedSeparator ? taintedSeparator->getRanges() : nullptr;
separatorLength = utils::GetCoercedLength(isolate, separatorArg);
}
}
auto arr = v8::Array::Cast(arrObj);

auto newRanges = getJoinResultRanges(isolate, transaction, arr, separatorRanges, separatorLength);
if (newRanges != nullptr) {
auto key = utils::GetLocalPointer(result);
transaction->AddTainted(key, newRanges, result);
args.GetReturnValue().Set(result);
return;
}
} catch (const std::bad_alloc& err) {
} catch (const container::QueuedPoolBadAlloc& err) {
} catch (const container::PoolBadAlloc& err) {
}
}
}
args.GetReturnValue().Set(args[1]);
}

void ArrayJoinOperations::Init(Local<Object> exports) {
NODE_SET_METHOD(exports, "arrayJoin", ArrayJoinOperator);
}
} // namespace api
} // namespace iast

21 changes: 21 additions & 0 deletions src/api/array_join.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
* This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
**/
#ifndef SRC_API_ARRAY_JOIN_H_
#define SRC_API_ARRAY_JOIN_H_

#include <node.h>
namespace iast {
namespace api {
class ArrayJoinOperations {
public:
static void Init(v8::Local<v8::Object> exports);

private:
ArrayJoinOperations();
~ArrayJoinOperations();
};
} // namespace api
} // namespace iast
#endif // SRC_API_ARRAY_JOIN_H_
90 changes: 90 additions & 0 deletions src/api/string_case.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
* This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
**/
#include <new>
#include <vector>
#include <memory>
#include <string>

#include "string_case.h"
#include "../tainted/range.h"
#include "../tainted/string_resource.h"
#include "../tainted/transaction.h"
#include "../iast.h"

#define TO_V8STRING(arg) (v8::Local<v8::String>::Cast(arg))

using v8::FunctionCallbackInfo;
using v8::Value;
using v8::Local;
using v8::Isolate;
using v8::Object;
using v8::String;

using iast::tainted::Range;

namespace iast {
namespace api {

void StringCaseOperator(const FunctionCallbackInfo<Value>& args) {
auto isolate = args.GetIsolate();

if (args.Length() < 3) {
isolate->ThrowException(v8::Exception::TypeError(
v8::String::NewFromUtf8(isolate,
"Wrong number of arguments",
v8::NewStringType::kNormal).ToLocalChecked()));
return;
}
if (!args[1]->IsString()) {
args.GetReturnValue().Set(args[1]);
return;
}

auto transaction = GetTransaction(utils::GetLocalPointer(args[0]));
if (transaction == nullptr) {
args.GetReturnValue().Set(args[1]);
return;
}

if (args[1] == args[2]) {
args.GetReturnValue().Set(args[1]);
return;
}

auto taintedObj = transaction->FindTaintedObject(utils::GetLocalPointer(args[2]));
if (!taintedObj) {
args.GetReturnValue().Set(args[1]);
return;
}

try {
auto ranges = taintedObj->getRanges();
if (ranges == nullptr) {
args.GetReturnValue().Set(args[1]);
return;
}

auto res = args[1];
int resultLength = TO_V8STRING(res)->Length();
if (resultLength == 1) {
res = tainted::NewExternalString(isolate, res);
}
auto key = utils::GetLocalPointer(res);
transaction->AddTainted(key, ranges, res);
args.GetReturnValue().Set(res);
return;
} catch (const std::bad_alloc& err) {
} catch (const container::QueuedPoolBadAlloc& err) {
} catch (const container::PoolBadAlloc& err) {
}
args.GetReturnValue().Set(args[1]);
}

void StringCaseOperations::Init(Local<Object> exports) {
NODE_SET_METHOD(exports, "stringCase", StringCaseOperator);
}
} // namespace api
} // namespace iast

21 changes: 21 additions & 0 deletions src/api/string_case.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
* This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
**/
#ifndef SRC_API_STRING_CASE_H_
#define SRC_API_STRING_CASE_H_

#include <node.h>
namespace iast {
namespace api {
class StringCaseOperations {
public:
static void Init(v8::Local<v8::Object> exports);

private:
StringCaseOperations();
~StringCaseOperations();
};
} // namespace api
} // namespace iast
#endif // SRC_API_STRING_CASE_H_
4 changes: 4 additions & 0 deletions src/iast.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "api/substring.h"
#include "api/replace.h"
#include "api/metrics.h"
#include "api/string_case.h"
#include "api/array_join.h"

using transactionManager = iast::container::Singleton<iast::TransactionManager<iast::tainted::Transaction,
iast::tainted::transaction_key_t>>;
Expand Down Expand Up @@ -50,6 +52,8 @@ void Init(v8::Local<v8::Object> exports) {
api::SliceOperations::Init(exports);
api::Substring::Init(exports);
api::ReplaceOperations::Init(exports);
api::StringCaseOperations::Init(exports);
api::ArrayJoinOperations::Init(exports);
api::Metrics::Init(exports);
exports->GetIsolate()->AddGCEpilogueCallback(iast::gc::OnScavenge, v8::GCType::kGCTypeScavenge);
exports->GetIsolate()->AddGCEpilogueCallback(iast::gc::OnMarkSweepCompact, v8::GCType::kGCTypeMarkSweepCompact);
Expand Down
Loading

0 comments on commit 288909f

Please sign in to comment.