@ogcio/o11y-sdk-node 0.4.2 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/sdk-core/index.d.ts +1 -0
- package/dist/sdk-core/index.js +1 -0
- package/dist/sdk-core/lib/index.d.ts +2 -0
- package/dist/sdk-core/lib/index.js +2 -0
- package/dist/sdk-core/lib/redaction/basic-redactor.d.ts +7 -0
- package/dist/sdk-core/lib/redaction/basic-redactor.js +18 -0
- package/dist/sdk-core/lib/redaction/email-redactor.d.ts +8 -0
- package/dist/sdk-core/lib/redaction/email-redactor.js +17 -0
- package/dist/sdk-core/lib/redaction/index.d.ts +9 -0
- package/dist/sdk-core/lib/redaction/index.js +4 -0
- package/dist/sdk-core/lib/redaction/ip-redactor.d.ts +9 -0
- package/dist/sdk-core/lib/redaction/ip-redactor.js +23 -0
- package/dist/sdk-core/lib/redaction/ppsn-redactor.d.ts +8 -0
- package/dist/sdk-core/lib/redaction/ppsn-redactor.js +17 -0
- package/dist/sdk-core/lib/utils/data-structures.d.ts +15 -0
- package/dist/{lib/internals/redaction/pii-detection.js → sdk-core/lib/utils/data-structures.js} +4 -27
- package/dist/sdk-core/lib/utils/index.d.ts +2 -0
- package/dist/sdk-core/lib/utils/index.js +2 -0
- package/dist/sdk-core/lib/utils/string-decoding.d.ts +7 -0
- package/dist/sdk-core/lib/utils/string-decoding.js +22 -0
- package/dist/{index.d.ts → sdk-node/index.d.ts} +1 -0
- package/dist/{index.js → sdk-node/index.js} +1 -0
- package/dist/{lib → sdk-node/lib}/config-manager.d.ts +1 -1
- package/dist/sdk-node/lib/exporter/console.d.ts +3 -0
- package/dist/sdk-node/lib/exporter/grpc.d.ts +3 -0
- package/dist/sdk-node/lib/exporter/http.d.ts +3 -0
- package/dist/{lib → sdk-node/lib}/exporter/index.d.ts +2 -2
- package/dist/{lib → sdk-node/lib}/exporter/pii-exporter-decorator.d.ts +5 -5
- package/dist/{lib → sdk-node/lib}/exporter/pii-exporter-decorator.js +6 -2
- package/dist/sdk-node/lib/exporter/processor-config.d.ts +7 -0
- package/dist/{lib → sdk-node/lib}/exporter/processor-config.js +4 -0
- package/dist/{lib → sdk-node/lib}/index.d.ts +11 -0
- package/dist/{lib → sdk-node/lib}/instrumentation.node.js +1 -1
- package/dist/sdk-node/lib/internals/redaction/redactors/email.d.ts +8 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/email.js +19 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/index.d.ts +16 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/index.js +13 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ip.d.ts +8 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ip.js +20 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ppsn.d.ts +8 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ppsn.js +18 -0
- package/dist/{lib → sdk-node/lib}/metrics.d.ts +1 -1
- package/dist/{lib → sdk-node/lib}/processor/enrich-logger-processor.d.ts +3 -3
- package/dist/{lib → sdk-node/lib}/processor/enrich-span-processor.d.ts +3 -3
- package/dist/sdk-node/lib/processor/nextjs-logger-processor.d.ts +7 -0
- package/dist/sdk-node/lib/processor/nextjs-logger-processor.js +30 -0
- package/dist/sdk-node/lib/processor/nextjs-span-processor.d.ts +8 -0
- package/dist/sdk-node/lib/processor/nextjs-span-processor.js +25 -0
- package/dist/{lib → sdk-node/lib}/resource.d.ts +2 -2
- package/dist/{lib → sdk-node/lib}/traces.d.ts +1 -1
- package/dist/{lib → sdk-node/lib}/traces.js +1 -1
- package/dist/{lib → sdk-node/lib}/url-sampler.d.ts +3 -3
- package/dist/{lib → sdk-node/lib}/utils.d.ts +1 -1
- package/dist/sdk-node/package.json +62 -0
- package/package.json +28 -25
- package/CHANGELOG.md +0 -233
- package/dist/lib/exporter/console.d.ts +0 -3
- package/dist/lib/exporter/grpc.d.ts +0 -3
- package/dist/lib/exporter/http.d.ts +0 -3
- package/dist/lib/exporter/processor-config.d.ts +0 -5
- package/dist/lib/internals/redaction/pii-detection.d.ts +0 -25
- package/dist/lib/internals/redaction/redactors/email.d.ts +0 -8
- package/dist/lib/internals/redaction/redactors/email.js +0 -48
- package/dist/lib/internals/redaction/redactors/index.d.ts +0 -4
- package/dist/lib/internals/redaction/redactors/index.js +0 -6
- package/dist/lib/internals/redaction/redactors/ip.d.ts +0 -10
- package/dist/lib/internals/redaction/redactors/ip.js +0 -54
- package/dist/lib/internals/shared-metrics.d.ts +0 -7
- package/dist/lib/internals/shared-metrics.js +0 -18
- package/dist/package.json +0 -59
- package/dist/vitest.config.d.ts +0 -2
- package/dist/vitest.config.js +0 -45
- package/index.ts +0 -9
- package/lib/config-manager.ts +0 -12
- package/lib/exporter/console.ts +0 -33
- package/lib/exporter/grpc.ts +0 -65
- package/lib/exporter/http.ts +0 -56
- package/lib/exporter/index.ts +0 -9
- package/lib/exporter/pii-exporter-decorator.ts +0 -187
- package/lib/exporter/processor-config.ts +0 -23
- package/lib/index.ts +0 -118
- package/lib/instrumentation.node.ts +0 -115
- package/lib/internals/hooks.ts +0 -14
- package/lib/internals/redaction/pii-detection.ts +0 -113
- package/lib/internals/redaction/redactors/email.ts +0 -58
- package/lib/internals/redaction/redactors/index.ts +0 -12
- package/lib/internals/redaction/redactors/ip.ts +0 -68
- package/lib/internals/shared-metrics.ts +0 -34
- package/lib/metrics.ts +0 -75
- package/lib/processor/enrich-logger-processor.ts +0 -34
- package/lib/processor/enrich-span-processor.ts +0 -39
- package/lib/resource.ts +0 -30
- package/lib/traces.ts +0 -78
- package/lib/url-sampler.ts +0 -52
- package/lib/utils.ts +0 -22
- package/test/config-manager.test.ts +0 -34
- package/test/exporter/pii-exporter-decorator.test.ts +0 -88
- package/test/index.test.ts +0 -70
- package/test/integration/README.md +0 -74
- package/test/integration/docker-utils.sh +0 -214
- package/test/integration/main.sh +0 -52
- package/test/integration/teardown.sh +0 -7
- package/test/integration/test_fastify-o11y-pii-enabled/http-tracing.integration.test.ts +0 -56
- package/test/integration/test_fastify-o11y-pii-enabled/pii.integration.test.ts +0 -68
- package/test/integration/test_fastify-o11y-pii-enabled/run.sh +0 -42
- package/test/integration/test_without-o11y/run.sh +0 -30
- package/test/integration/test_without-o11y/verify-status.integration.test.ts +0 -32
- package/test/internals/hooks.test.ts +0 -45
- package/test/internals/pii-detection.test.ts +0 -265
- package/test/internals/redactors/email.test.ts +0 -81
- package/test/internals/redactors/ip.test.ts +0 -93
- package/test/internals/shared-metrics.test.ts +0 -34
- package/test/metrics.test.ts +0 -142
- package/test/node-config.test.ts +0 -190
- package/test/processor/enrich-logger-processor.test.ts +0 -58
- package/test/processor/enrich-span-processor.test.ts +0 -52
- package/test/resource.test.ts +0 -33
- package/test/traces/active-span.test.ts +0 -26
- package/test/traces/with-span.test.ts +0 -356
- package/test/url-sampler.test.ts +0 -215
- package/test/utils/alloy-log-parser.ts +0 -53
- package/test/utils/mock-signals.ts +0 -144
- package/test/validation.test.ts +0 -103
- package/tsconfig.json +0 -15
- package/vitest.config.ts +0 -46
- /package/dist/{lib → sdk-node/lib}/config-manager.js +0 -0
- /package/dist/{lib → sdk-node/lib}/exporter/console.js +0 -0
- /package/dist/{lib → sdk-node/lib}/exporter/grpc.js +0 -0
- /package/dist/{lib → sdk-node/lib}/exporter/http.js +0 -0
- /package/dist/{lib → sdk-node/lib}/exporter/index.js +0 -0
- /package/dist/{lib → sdk-node/lib}/index.js +0 -0
- /package/dist/{lib → sdk-node/lib}/instrumentation.node.d.ts +0 -0
- /package/dist/{lib → sdk-node/lib}/internals/hooks.d.ts +0 -0
- /package/dist/{lib → sdk-node/lib}/internals/hooks.js +0 -0
- /package/dist/{lib → sdk-node/lib}/metrics.js +0 -0
- /package/dist/{lib → sdk-node/lib}/processor/enrich-logger-processor.js +0 -0
- /package/dist/{lib → sdk-node/lib}/processor/enrich-span-processor.js +0 -0
- /package/dist/{lib → sdk-node/lib}/resource.js +0 -0
- /package/dist/{lib → sdk-node/lib}/url-sampler.js +0 -0
- /package/dist/{lib → sdk-node/lib}/utils.js +0 -0
package/test/integration/main.sh
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
set -e
|
|
4
|
-
set -o pipefail
|
|
5
|
-
|
|
6
|
-
if [ $# -lt 2 ]; then
|
|
7
|
-
echo "Usage: $0 <build-id> <root-path>"
|
|
8
|
-
exit 1
|
|
9
|
-
fi
|
|
10
|
-
|
|
11
|
-
BUILD_ID=$1
|
|
12
|
-
ROOT_PATH=$2
|
|
13
|
-
|
|
14
|
-
NETWORK_NAME="${BUILD_ID}_testnetwork"
|
|
15
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
-
|
|
17
|
-
docker network create $NETWORK_NAME
|
|
18
|
-
|
|
19
|
-
TEST_SCRIPTS=( $(find . -type f -path "./packages/sdk-node/test/integration/test*/run.sh" | sort) )
|
|
20
|
-
|
|
21
|
-
FAILURES=0
|
|
22
|
-
|
|
23
|
-
for test_script in "${TEST_SCRIPTS[@]}"; do
|
|
24
|
-
TEST_DIR=$(dirname "$test_script")
|
|
25
|
-
TEST_NAME=$(basename "$TEST_DIR")
|
|
26
|
-
|
|
27
|
-
echo "Running test: $TEST_NAME ($test_script)"
|
|
28
|
-
|
|
29
|
-
# Run the test
|
|
30
|
-
bash "$test_script" "$BUILD_ID" "$ROOT_PATH" "$NETWORK_NAME"
|
|
31
|
-
EXIT_CODE=$?
|
|
32
|
-
|
|
33
|
-
if [ $EXIT_CODE -eq 0 ]; then
|
|
34
|
-
echo "$test_script completed"
|
|
35
|
-
else
|
|
36
|
-
echo "❌ $test_script failed with exit code $EXIT_CODE"
|
|
37
|
-
FAILURES=$((FAILURES + 1))
|
|
38
|
-
fi
|
|
39
|
-
echo "------------------------------"
|
|
40
|
-
done
|
|
41
|
-
|
|
42
|
-
# Remove network and images from docker host
|
|
43
|
-
bash "$SCRIPT_DIR/teardown.sh" "$BUILD_ID" "$NETWORK_NAME"
|
|
44
|
-
|
|
45
|
-
# Final summary
|
|
46
|
-
if [ $FAILURES -eq 0 ]; then
|
|
47
|
-
echo "All tests ready!"
|
|
48
|
-
exit 0
|
|
49
|
-
else
|
|
50
|
-
echo "$FAILURES test(s) failed."
|
|
51
|
-
exit 1
|
|
52
|
-
fi
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { describe, test, assert } from "vitest";
|
|
2
|
-
import { parseLog } from "../../utils/alloy-log-parser";
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
|
|
6
|
-
describe("instrumentation integration test", () => {
|
|
7
|
-
test("should exclude health url and process only dummy calls", async () => {
|
|
8
|
-
const data = await readFile(join(__dirname, "logs.txt"), "utf-8");
|
|
9
|
-
|
|
10
|
-
let health_traces_counter = 0;
|
|
11
|
-
let dummy_traces_counter = 0;
|
|
12
|
-
|
|
13
|
-
for (const line of data.split(/\nts=/)) {
|
|
14
|
-
const parsedLine: Record<string, object | string | number> =
|
|
15
|
-
parseLog(line);
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
parsedLine["attributes"] &&
|
|
19
|
-
parsedLine["attributes"]["span_kind"] &&
|
|
20
|
-
parsedLine["attributes"]["span_kind"] === "trace"
|
|
21
|
-
) {
|
|
22
|
-
if (parsedLine["attributes"]["http.target"] === "/api/dummy") {
|
|
23
|
-
dummy_traces_counter++;
|
|
24
|
-
|
|
25
|
-
// verify global sdk span resource
|
|
26
|
-
assert.equal(
|
|
27
|
-
parsedLine["resource_attributes"]["team.infra.pod"],
|
|
28
|
-
"01",
|
|
29
|
-
);
|
|
30
|
-
assert.equal(
|
|
31
|
-
parsedLine["resource_attributes"]["team.service.type"],
|
|
32
|
-
"fastify",
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
// verify global sdk span attributes
|
|
36
|
-
assert.equal(parsedLine["attributes"]["signal.namespace"], "example");
|
|
37
|
-
|
|
38
|
-
// verify runtime custom span inside dev application
|
|
39
|
-
assert.equal(parsedLine["attributes"]["business.info"], "dummy");
|
|
40
|
-
assert.equal(
|
|
41
|
-
parsedLine["attributes"]["business.request_type"],
|
|
42
|
-
"application/json",
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (parsedLine["attributes"]["http.target"] === "/api/health") {
|
|
48
|
-
health_traces_counter++;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
assert.equal(health_traces_counter, 0);
|
|
54
|
-
assert.equal(dummy_traces_counter, 2);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { describe, test, assert } from "vitest";
|
|
2
|
-
import { parseLog } from "../../utils/alloy-log-parser";
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
|
|
6
|
-
describe("pii integration test", () => {
|
|
7
|
-
test("should check redacted attributes and detect zero emails in logs", async () => {
|
|
8
|
-
const data = await readFile(join(__dirname, "logs.txt"), "utf-8");
|
|
9
|
-
|
|
10
|
-
let health_traces_counter = 0;
|
|
11
|
-
let dummy_traces_counter = 0;
|
|
12
|
-
let email_not_redacted = 0;
|
|
13
|
-
|
|
14
|
-
for (const line of data.split(/\nts=/)) {
|
|
15
|
-
const parsedLine: Record<string, object | string | number> =
|
|
16
|
-
parseLog(line);
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
parsedLine["attributes"] &&
|
|
20
|
-
parsedLine["attributes"]["span_kind"] &&
|
|
21
|
-
parsedLine["attributes"]["span_kind"] === "log"
|
|
22
|
-
) {
|
|
23
|
-
const matches = (parsedLine["log_body"] as string).match(
|
|
24
|
-
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
25
|
-
);
|
|
26
|
-
if (matches) {
|
|
27
|
-
email_not_redacted++;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
parsedLine["attributes"] &&
|
|
33
|
-
parsedLine["attributes"]["span_kind"] &&
|
|
34
|
-
parsedLine["attributes"]["span_kind"] === "trace"
|
|
35
|
-
) {
|
|
36
|
-
if (parsedLine["attributes"]["http.target"] === "/api/dummy") {
|
|
37
|
-
dummy_traces_counter++;
|
|
38
|
-
|
|
39
|
-
// verify global sdk span resource
|
|
40
|
-
assert.equal(
|
|
41
|
-
parsedLine["resource_attributes"]["email"],
|
|
42
|
-
"[REDACTED EMAIL]",
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
// verify global sdk span attributes
|
|
46
|
-
assert.equal(parsedLine["attributes"]["email"], "[REDACTED EMAIL]");
|
|
47
|
-
// verify custom runtime span attributes
|
|
48
|
-
assert.equal(
|
|
49
|
-
parsedLine["attributes"]["multiple.email"],
|
|
50
|
-
"[REDACTED EMAIL]",
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
// verify global sdk span attributes
|
|
54
|
-
assert.equal(parsedLine["events"]["email"], "[REDACTED EMAIL]");
|
|
55
|
-
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
if (parsedLine["attributes"]["http.target"] === "/api/health") {
|
|
59
|
-
health_traces_counter++;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
assert.equal(email_not_redacted, 0);
|
|
65
|
-
assert.equal(health_traces_counter, 0);
|
|
66
|
-
assert.equal(dummy_traces_counter, 2);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# @docs: packages/sdk-node/test/integration/README.md#test_fastify-o11y-pii-enabled
|
|
3
|
-
BUILD_ID=$1
|
|
4
|
-
ROOT_PATH=$2
|
|
5
|
-
NETWORK_NAME=$3
|
|
6
|
-
|
|
7
|
-
ALLOY_CONTAINER_NAME="integrationalloy"
|
|
8
|
-
NODE_CONTAINER_NAME="${BUILD_ID}_fastify_app"
|
|
9
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
-
LOG_FILE="$SCRIPT_DIR/logs.txt"
|
|
11
|
-
ERROR_CODE=0
|
|
12
|
-
|
|
13
|
-
source "$SCRIPT_DIR/../docker-utils.sh"
|
|
14
|
-
|
|
15
|
-
build_image "${NODE_CONTAINER_NAME}:${BUILD_ID}" "$ROOT_PATH/examples/fastify/Dockerfile" "$ROOT_PATH/"
|
|
16
|
-
|
|
17
|
-
run_and_check_alloy $ROOT_PATH $NETWORK_NAME $ALLOY_CONTAINER_NAME
|
|
18
|
-
ERROR_CODE=$?
|
|
19
|
-
|
|
20
|
-
if [[ $ERROR_CODE -eq 0 ]]; then
|
|
21
|
-
run_and_check_fastify $NETWORK_NAME "http://integrationalloy:4317" $NODE_CONTAINER_NAME $BUILD_ID
|
|
22
|
-
ERROR_CODE=$?
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
if [[ $ERROR_CODE -eq 0 ]]; then
|
|
26
|
-
sleep 2
|
|
27
|
-
curl -X GET -f http://localhost:9091/api/dummy -H 'Content-Type: application/json'
|
|
28
|
-
sleep 2
|
|
29
|
-
curl -X GET -f http://localhost:9091/api/dummy -H 'Content-Type: application/json'
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
# sleep N seconds to await instrumentation flow send and receiving signals
|
|
33
|
-
sleep 1
|
|
34
|
-
|
|
35
|
-
# Copy logs from container to file
|
|
36
|
-
docker container logs $ALLOY_CONTAINER_NAME >&$LOG_FILE
|
|
37
|
-
echo "log file at $LOG_FILE"
|
|
38
|
-
|
|
39
|
-
container_stop_and_rm $ALLOY_CONTAINER_NAME
|
|
40
|
-
container_stop_and_rm $NODE_CONTAINER_NAME
|
|
41
|
-
|
|
42
|
-
exit $ERROR_CODE
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# @docs: packages/sdk-node/test/integration/README.md#test_without-o11y
|
|
3
|
-
BUILD_ID=$1
|
|
4
|
-
ROOT_PATH=$2
|
|
5
|
-
NETWORK_NAME=$3
|
|
6
|
-
|
|
7
|
-
NODE_CONTAINER_NAME="${BUILD_ID}_fastify_app"
|
|
8
|
-
ERROR_CODE=0
|
|
9
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
-
LOG_FILE="$SCRIPT_DIR/logs.txt"
|
|
11
|
-
|
|
12
|
-
source "$SCRIPT_DIR/../docker-utils.sh"
|
|
13
|
-
|
|
14
|
-
build_image "${NODE_CONTAINER_NAME}:${BUILD_ID}" "$ROOT_PATH/examples/fastify/Dockerfile" "$ROOT_PATH/"
|
|
15
|
-
|
|
16
|
-
run_and_check_fastify $NETWORK_NAME "" $NODE_CONTAINER_NAME $BUILD_ID
|
|
17
|
-
ERROR_CODE=$?
|
|
18
|
-
|
|
19
|
-
if [[ $ERROR_CODE -eq 0 ]]; then
|
|
20
|
-
curl -X GET -f http://localhost:9091/api/dummy -H 'Content-Type: application/json'
|
|
21
|
-
sleep 1
|
|
22
|
-
curl -X GET -f http://localhost:9091/api/dummy -H 'Content-Type: application/json'
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
docker container logs $NODE_CONTAINER_NAME >&$LOG_FILE
|
|
26
|
-
echo "log file at $LOG_FILE"
|
|
27
|
-
|
|
28
|
-
container_stop_and_rm $NODE_CONTAINER_NAME
|
|
29
|
-
|
|
30
|
-
exit $ERROR_CODE
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
|
|
5
|
-
describe("Log validation for /api/dummy with instrumentation disabled", async () => {
|
|
6
|
-
const log = await readFile(join(__dirname, "logs.txt"), "utf-8");
|
|
7
|
-
|
|
8
|
-
it("should contain message that collector URL is not set", () => {
|
|
9
|
-
expect(log).toContain(
|
|
10
|
-
"collectorUrl not set. Skipping NodeJS OpenTelemetry instrumentation.",
|
|
11
|
-
);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("should have all /api/dummy responses with status code 200", () => {
|
|
15
|
-
// Extract all request IDs for /api/dummy requests
|
|
16
|
-
const dummyRequestIds = [
|
|
17
|
-
...log.matchAll(/"reqId":"(.*?)","req":\{[^}]*"url":"\/api\/dummy"/g),
|
|
18
|
-
].map((match) => match[1]);
|
|
19
|
-
|
|
20
|
-
expect(dummyRequestIds.length).toBeGreaterThan(0);
|
|
21
|
-
|
|
22
|
-
// For each /api/dummy request ID, verify that its response status is 200
|
|
23
|
-
const non200Responses = dummyRequestIds.filter((reqId) => {
|
|
24
|
-
const responseMatch = log.match(
|
|
25
|
-
new RegExp(`"reqId":"${reqId}".*"statusCode":(\\d+)`),
|
|
26
|
-
);
|
|
27
|
-
return responseMatch && Number(responseMatch[1]) !== 200;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
expect(non200Responses).toEqual([]); // No non-200 responses allowed
|
|
31
|
-
});
|
|
32
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { _shutdownHook } from "../../lib/internals/hooks";
|
|
3
|
-
|
|
4
|
-
describe("shutdown hook", () => {
|
|
5
|
-
it("should call sdk.shutdown and log success on SIGTERM", async () => {
|
|
6
|
-
const shutdownMock = vi.fn().mockResolvedValue(undefined);
|
|
7
|
-
const consoleLogMock = vi
|
|
8
|
-
.spyOn(console, "log")
|
|
9
|
-
.mockImplementation(() => {});
|
|
10
|
-
|
|
11
|
-
_shutdownHook({ shutdown: shutdownMock });
|
|
12
|
-
|
|
13
|
-
process.emit("SIGTERM");
|
|
14
|
-
// Wait for async logic
|
|
15
|
-
await Promise.resolve();
|
|
16
|
-
|
|
17
|
-
expect(shutdownMock).toHaveBeenCalled();
|
|
18
|
-
expect(consoleLogMock).toHaveBeenCalledWith(
|
|
19
|
-
"NodeJS OpenTelemetry instrumentation shutdown successfully",
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
consoleLogMock.mockRestore();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("should catch and log errors on shutdown failure", async () => {
|
|
26
|
-
const error = new Error("Shutdown failed");
|
|
27
|
-
const shutdownMock = vi.fn().mockRejectedValue(error);
|
|
28
|
-
const consoleErrorMock = vi
|
|
29
|
-
.spyOn(console, "error")
|
|
30
|
-
.mockImplementation(() => {});
|
|
31
|
-
|
|
32
|
-
_shutdownHook({ shutdown: shutdownMock });
|
|
33
|
-
|
|
34
|
-
process.emit("SIGTERM");
|
|
35
|
-
// Wait for async logic
|
|
36
|
-
await Promise.resolve();
|
|
37
|
-
|
|
38
|
-
expect(consoleErrorMock).toHaveBeenCalledWith(
|
|
39
|
-
"Error shutting down NodeJS OpenTelemetry instrumentation:",
|
|
40
|
-
error,
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
consoleErrorMock.mockRestore();
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi, beforeEach } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
_cleanStringPII,
|
|
4
|
-
_containsEncodedComponents,
|
|
5
|
-
_recursiveObjectClean,
|
|
6
|
-
} from "../../lib/internals/redaction/pii-detection.js";
|
|
7
|
-
import * as sharedMetrics from "../../lib/internals/shared-metrics.js";
|
|
8
|
-
import { emailRedactor } from "../../lib/internals/redaction/redactors/email";
|
|
9
|
-
import { ipRedactor } from "../../lib/internals/redaction/redactors/ip";
|
|
10
|
-
|
|
11
|
-
describe("PII Detection Utils", () => {
|
|
12
|
-
const mockMetricAdd = vi.fn();
|
|
13
|
-
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
vi.restoreAllMocks();
|
|
16
|
-
vi.spyOn(sharedMetrics, "_getPIICounterRedactionMetric").mockReturnValue({
|
|
17
|
-
add: mockMetricAdd,
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
describe("_cleanStringPII", () => {
|
|
22
|
-
it("redacts plain PII", () => {
|
|
23
|
-
const input = "admin@example.com";
|
|
24
|
-
const output = _cleanStringPII(input, "log", [emailRedactor]);
|
|
25
|
-
|
|
26
|
-
expect(output).toBe("[REDACTED EMAIL]");
|
|
27
|
-
expect(mockMetricAdd).toHaveBeenCalledWith(
|
|
28
|
-
1,
|
|
29
|
-
expect.objectContaining({
|
|
30
|
-
pii_email_domain: "example.com",
|
|
31
|
-
pii_type: "email",
|
|
32
|
-
redaction_source: "log",
|
|
33
|
-
}),
|
|
34
|
-
);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("redacts email in URL-encoded string", () => {
|
|
38
|
-
const input = "user%40gmail.com";
|
|
39
|
-
const output = _cleanStringPII(input, "log", [emailRedactor]);
|
|
40
|
-
|
|
41
|
-
expect(output).toBe("[REDACTED EMAIL]");
|
|
42
|
-
expect(mockMetricAdd).toHaveBeenCalledWith(
|
|
43
|
-
1,
|
|
44
|
-
expect.objectContaining({
|
|
45
|
-
pii_format: "url",
|
|
46
|
-
pii_email_domain: "gmail.com",
|
|
47
|
-
}),
|
|
48
|
-
);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("redacts ip in URL-encoded string", () => {
|
|
52
|
-
const input = "%20127.0.0.1";
|
|
53
|
-
const output = _cleanStringPII(input, "log", [ipRedactor]);
|
|
54
|
-
|
|
55
|
-
expect(output).toBe(" [REDACTED IPV4]");
|
|
56
|
-
expect(mockMetricAdd).toHaveBeenCalledWith(
|
|
57
|
-
1,
|
|
58
|
-
expect.objectContaining({
|
|
59
|
-
pii_format: "url",
|
|
60
|
-
pii_type: "IPv4",
|
|
61
|
-
redaction_source: "log",
|
|
62
|
-
}),
|
|
63
|
-
);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("handles strings without PII unchanged", () => {
|
|
67
|
-
const input = "hello world";
|
|
68
|
-
const output = _cleanStringPII(input, "log", [emailRedactor]);
|
|
69
|
-
|
|
70
|
-
expect(output).toBe("hello world");
|
|
71
|
-
expect(mockMetricAdd).not.toHaveBeenCalled();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("ignores non-string input", () => {
|
|
75
|
-
// @ts-expect-error
|
|
76
|
-
expect(_cleanStringPII(1234, "trace", [emailRedactor])).toBe(1234);
|
|
77
|
-
// @ts-expect-error
|
|
78
|
-
expect(_cleanStringPII(true, "trace", [emailRedactor])).toBe(true);
|
|
79
|
-
expect(
|
|
80
|
-
_cleanStringPII(undefined, "trace", [emailRedactor]),
|
|
81
|
-
).toBeUndefined();
|
|
82
|
-
expect(mockMetricAdd).not.toHaveBeenCalled();
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
describe("_recursiveObjectClean", () => {
|
|
87
|
-
it("cleans string PII", () => {
|
|
88
|
-
const result = _recursiveObjectClean("demo@abc.com", "log", [
|
|
89
|
-
emailRedactor,
|
|
90
|
-
]);
|
|
91
|
-
expect(result).toBe("[REDACTED EMAIL]");
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("cleans array of strings", () => {
|
|
95
|
-
const input = ["one@gmail.com", "two@example.com"];
|
|
96
|
-
const output = _recursiveObjectClean(input, "log", [emailRedactor]);
|
|
97
|
-
|
|
98
|
-
expect(output).toEqual(["[REDACTED EMAIL]", "[REDACTED EMAIL]"]);
|
|
99
|
-
expect(mockMetricAdd).toHaveBeenCalledTimes(2);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("cleans deeply nested object", () => {
|
|
103
|
-
const input = {
|
|
104
|
-
user: {
|
|
105
|
-
email: "test@gmail.com",
|
|
106
|
-
profile: {
|
|
107
|
-
contact: "foo@example.com",
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
status: "active",
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const result = _recursiveObjectClean(input, "log", [emailRedactor]);
|
|
114
|
-
|
|
115
|
-
expect(result).toEqual({
|
|
116
|
-
user: {
|
|
117
|
-
email: "[REDACTED EMAIL]",
|
|
118
|
-
profile: {
|
|
119
|
-
contact: "[REDACTED EMAIL]",
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
status: "active",
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("cleans Uint8Array input", () => {
|
|
127
|
-
const str = "admin@gmail.com";
|
|
128
|
-
const buffer = new TextEncoder().encode(str);
|
|
129
|
-
const result = _recursiveObjectClean(buffer, "log", [emailRedactor]);
|
|
130
|
-
const decoded = new TextDecoder().decode(result as Uint8Array);
|
|
131
|
-
|
|
132
|
-
expect(decoded).toBe("[REDACTED EMAIL]");
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("skips malformed Uint8Array decode", () => {
|
|
136
|
-
const corrupted = new Uint8Array([0xff, 0xfe, 0xfd]);
|
|
137
|
-
const result = _recursiveObjectClean(corrupted, "log", [emailRedactor]);
|
|
138
|
-
|
|
139
|
-
// Should return a Uint8Array, but unmodified/redaction should not happen
|
|
140
|
-
expect(result).toBeInstanceOf(Uint8Array);
|
|
141
|
-
expect(result).not.toEqual(expect.arrayContaining([91, 82, 69]));
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("cleans arrays of values", () => {
|
|
145
|
-
const result = _recursiveObjectClean(
|
|
146
|
-
["bob@abc.com", 123, { nested: "jane@example.com" }],
|
|
147
|
-
"log",
|
|
148
|
-
[emailRedactor],
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
expect(result).toEqual([
|
|
152
|
-
"[REDACTED EMAIL]",
|
|
153
|
-
123,
|
|
154
|
-
{ nested: "[REDACTED EMAIL]" },
|
|
155
|
-
]);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("passes null and boolean through", () => {
|
|
159
|
-
expect(_recursiveObjectClean(null, "log", [emailRedactor])).toBeNull();
|
|
160
|
-
expect(
|
|
161
|
-
_recursiveObjectClean(undefined, "log", [emailRedactor]),
|
|
162
|
-
).toBeUndefined();
|
|
163
|
-
expect(_recursiveObjectClean(true, "log", [emailRedactor])).toBe(true);
|
|
164
|
-
expect(_recursiveObjectClean(false, "log", [emailRedactor])).toBe(false);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
describe("_containsEncodedComponents", () => {
|
|
169
|
-
describe("should return true for properly URL encoded strings", () => {
|
|
170
|
-
it.each([
|
|
171
|
-
["hello%20world", "Space encoded as %20"],
|
|
172
|
-
["test%2Bvalue", "Plus sign encoded as %2B"],
|
|
173
|
-
["path%2Fto%2Ffile", "Forward slashes encoded"],
|
|
174
|
-
["user%40domain.com", "@ symbol encoded"],
|
|
175
|
-
["100%25%20off", "Percent and space encoded"],
|
|
176
|
-
["a%3Db%26c%3Dd", "Query parameters (a=b&c=d)"],
|
|
177
|
-
["caf%C3%A9", "UTF-8 encoded (café)"],
|
|
178
|
-
["price%3A%20%2410", "Colon, space, dollar ($10)"],
|
|
179
|
-
["%22quoted%22", "Double quotes encoded"],
|
|
180
|
-
["https%3A%2F%2Fexample.com", "Full URL encoded"],
|
|
181
|
-
["file%20name.txt", "filename encoded"],
|
|
182
|
-
["search%3Fq%3Dhello%20world", "Query string encoded"],
|
|
183
|
-
["%3C%3E%26%22%27", "HTML special chars encoded (<>&\"')"],
|
|
184
|
-
["%E2%9C%93", "UTF-8 checkmark (✓) encoded"],
|
|
185
|
-
["test%2b", "Lowercase hex"],
|
|
186
|
-
["%E4%B8%AD%E6%96%87", "Chinese characters encoded"],
|
|
187
|
-
])('should detect "%s" as URL encoded (%s)', (input, description) => {
|
|
188
|
-
expect(_containsEncodedComponents(input)).toBe(true);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
describe("should return false for non-URL encoded strings", () => {
|
|
193
|
-
it.each([
|
|
194
|
-
["test", "Simple ASCII string"],
|
|
195
|
-
["hello world", "Unencoded space"],
|
|
196
|
-
["user@domain.com", "Unencoded email"],
|
|
197
|
-
["simple123", "Alphanumeric only"],
|
|
198
|
-
["", "Empty string"],
|
|
199
|
-
["25%%", "Literal percent signs"],
|
|
200
|
-
["100% off", "Percent without hex digits"],
|
|
201
|
-
["test%2", "Incomplete percent encoding"],
|
|
202
|
-
["hello%ZZ", "Invalid hex digits"],
|
|
203
|
-
["test%2G", "Invalid hex digit G"],
|
|
204
|
-
["bad%encoding%here", "Percent without hex pairs"],
|
|
205
|
-
["hello%20world and more", "Partially encoded string"],
|
|
206
|
-
["hello%20world%21%20how%20are%20you", "Overly encoded string"],
|
|
207
|
-
["café", "Unicode characters (unencoded)"],
|
|
208
|
-
["hello+world", "Plus sign (form encoding style)"],
|
|
209
|
-
["test%", "Trailing percent"],
|
|
210
|
-
["hello%20world%", "Encoded content with trailing percent"],
|
|
211
|
-
["%", "Single percent"],
|
|
212
|
-
["%%", "Double percent"],
|
|
213
|
-
["normal text with % symbols", "Text with percent but no encoding"],
|
|
214
|
-
["price: $100%", "Currency with percent"],
|
|
215
|
-
["file.txt", "Simple filename"],
|
|
216
|
-
["path/to/file", "Unencoded path"],
|
|
217
|
-
["query?param=value", "Unencoded query string"],
|
|
218
|
-
["hello%world", "Percent without following hex"],
|
|
219
|
-
["test%1", "Single hex digit after percent"],
|
|
220
|
-
["hello%zz", "Non-hex characters after percent"],
|
|
221
|
-
])('should not detect "%s" as URL encoded (%s)', (input, description) => {
|
|
222
|
-
expect(_containsEncodedComponents(input)).toBe(false);
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
describe("Error handling", () => {
|
|
227
|
-
it.each([
|
|
228
|
-
["%C0%80", "Overlong UTF-8 encoding (security concern)"],
|
|
229
|
-
["%ED%A0%80", "UTF-8 surrogate (invalid)"],
|
|
230
|
-
["%FF%FE", "Invalid UTF-8 sequence"],
|
|
231
|
-
["test%20%ZZ", "Mix of valid and invalid encoding"],
|
|
232
|
-
])('should handle "%s" (%s)', (input, description) => {
|
|
233
|
-
// These should not throw errors
|
|
234
|
-
expect(() => _containsEncodedComponents(input)).not.toThrow();
|
|
235
|
-
|
|
236
|
-
// Most of these should return false due to invalid sequences
|
|
237
|
-
// or security-related encoding issues
|
|
238
|
-
const result = _containsEncodedComponents(input);
|
|
239
|
-
expect(typeof result).toBe("boolean");
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
describe("real-world url examples", () => {
|
|
244
|
-
it.each([
|
|
245
|
-
[
|
|
246
|
-
"https%3A%2F%2Fgoogle.com%2Fsearch%3Fq%3Djavascript",
|
|
247
|
-
"Encoded Google search URL",
|
|
248
|
-
],
|
|
249
|
-
["The%20quick%20brown%20fox", "Sentence with spaces encoded"],
|
|
250
|
-
[
|
|
251
|
-
"/api/user?body=here%20are%20all%20my%20secrets",
|
|
252
|
-
"contextually encoded API path",
|
|
253
|
-
],
|
|
254
|
-
["redirect_uri=https%3A%2F%2Fapp.com%2Fcallback", "OAuth redirect URI"],
|
|
255
|
-
["data%3Atext%2Fplain%3Bbase64%2CSGVsbG8%3D", "Data URL encoded"],
|
|
256
|
-
])('real-world case: "%s" (%s)', (input, description) => {
|
|
257
|
-
const result = _containsEncodedComponents(input);
|
|
258
|
-
|
|
259
|
-
// Verify the function doesn't crash
|
|
260
|
-
expect(typeof result).toBe("boolean");
|
|
261
|
-
expect(result).toBe(true);
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
});
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
describe,
|
|
3
|
-
expect,
|
|
4
|
-
it,
|
|
5
|
-
vi,
|
|
6
|
-
beforeEach,
|
|
7
|
-
beforeAll,
|
|
8
|
-
afterAll,
|
|
9
|
-
} from "vitest";
|
|
10
|
-
|
|
11
|
-
import * as sharedMetrics from "../../../lib/internals/shared-metrics.js";
|
|
12
|
-
import { ipRedactor } from "../../../lib/internals/redaction/redactors/ip";
|
|
13
|
-
import { emailRedactor } from "../../../lib/internals/redaction/redactors/email";
|
|
14
|
-
|
|
15
|
-
describe("Email Redaction utils", () => {
|
|
16
|
-
describe("tracks metrics", () => {
|
|
17
|
-
const mockMetricAdd = vi.fn();
|
|
18
|
-
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
vi.restoreAllMocks();
|
|
21
|
-
vi.spyOn(sharedMetrics, "_getPIICounterRedactionMetric").mockReturnValue({
|
|
22
|
-
add: mockMetricAdd,
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("redacts plain PII and tracks redaction with metric", () => {
|
|
27
|
-
const input = "admin@example.com";
|
|
28
|
-
const output = emailRedactor(input, "log", "string");
|
|
29
|
-
|
|
30
|
-
expect(output).toBe("[REDACTED EMAIL]");
|
|
31
|
-
expect(mockMetricAdd).toHaveBeenCalledWith(
|
|
32
|
-
1,
|
|
33
|
-
expect.objectContaining({
|
|
34
|
-
pii_email_domain: "example.com",
|
|
35
|
-
pii_type: "email",
|
|
36
|
-
redaction_source: "log",
|
|
37
|
-
}),
|
|
38
|
-
);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("handles strings without PII unchanged", () => {
|
|
42
|
-
const input = "hello world";
|
|
43
|
-
const output = ipRedactor(input, "log", "string");
|
|
44
|
-
|
|
45
|
-
expect(output).toBe("hello world");
|
|
46
|
-
expect(mockMetricAdd).not.toHaveBeenCalled();
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe("Redacts email addresses", () => {
|
|
51
|
-
beforeAll(() => {
|
|
52
|
-
vi.spyOn(sharedMetrics, "_getPIICounterRedactionMetric").mockReturnValue({
|
|
53
|
-
add: vi.fn(),
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
afterAll(() => {
|
|
58
|
-
vi.restoreAllMocks();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it.each`
|
|
62
|
-
value | expectedRedactedValue
|
|
63
|
-
${"user+tag@example.com"} | ${"[REDACTED EMAIL]"}
|
|
64
|
-
${"user.name+tag+sorting@example.com"} | ${"[REDACTED EMAIL]"}
|
|
65
|
-
${"x@example.museum"} | ${"[REDACTED EMAIL]"}
|
|
66
|
-
${"a.b-c_d@example.co.uk"} | ${"[REDACTED EMAIL]"}
|
|
67
|
-
${"üser@example.de"} | ${"[REDACTED EMAIL]"}
|
|
68
|
-
${"john.doe@xn--exmple-cua.com"} | ${"[REDACTED EMAIL]"}
|
|
69
|
-
${"üser@example.de"} | ${"[REDACTED EMAIL]"}
|
|
70
|
-
${"plainaddress"} | ${"plainaddress"}
|
|
71
|
-
${"@missinglocal.org"} | ${"@missinglocal.org"}
|
|
72
|
-
${"user@invalid_domain.com"} | ${"user@invalid_domain.com"}
|
|
73
|
-
`(
|
|
74
|
-
"returns $expectedRedactedValue for value '$value'",
|
|
75
|
-
async ({ value, expectedRedactedValue }: Record<string, string>) => {
|
|
76
|
-
const result = emailRedactor(value, "log", "string");
|
|
77
|
-
expect(result).toBe(expectedRedactedValue);
|
|
78
|
-
},
|
|
79
|
-
);
|
|
80
|
-
});
|
|
81
|
-
});
|