Skip to content
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

Add propagation string case functions and Array.join function #98

Merged
merged 15 commits into from
Mar 26, 2024
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) {
uurien marked this conversation as resolved.
Show resolved Hide resolved
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
Loading