Skip to content
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
23 changes: 20 additions & 3 deletions lib/telemetry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,25 @@ get_telemetry_dir() {
echo "${HOME}/.config/1Password/data/hook-events"
}

# Emit the `,"ts":<epoch_ms>` JSON fragment stamping when the event was
# produced, or an empty string if the clock is unreadable. Guarded so a non-numeric clock never yields a malformed
# line.
ts_json_fragment() {
local ts
ts=$(current_time_ms)
if [[ "$ts" =~ ^[0-9]+$ ]]; then
printf ',"ts":%s' "$ts"
fi
}

# Check whether the 1Password app has signaled that telemetry is enabled.
# Returns 0 (true) if the signal file exists, 1 (false) otherwise.
telemetry_consent_enabled() {
[[ -f "${HOME}/.config/1Password/telemetry-enabled" ]]
}

# Append a single JSON line to the events.jsonl file.
# Checks consent and enforces a 1MB file size cap.
# Checks consent and enforces a 1MB file size cap (drop-newest).
write_telemetry_event() {
local json_line="$1"
local event_dir
Expand Down Expand Up @@ -143,8 +154,11 @@ write_execution_event() {
local duration_bucket
duration_bucket=$(bucket_duration_ms "$duration_ms")

local ts_field
ts_field=$(ts_json_fragment)

local json_line
json_line="{\"schema\":\"agent_hook_execution\",\"hook_name\":\"${escaped_hook_name}\",\"hook_version\":\"${escaped_hook_version}\",\"client\":\"${escaped_client}\",\"event_type\":\"${escaped_event_type}\",\"decision\":\"${decision}\",\"deny_reason\":${deny_reason_json},\"duration_bucket\":\"${duration_bucket}\",\"mode\":${mode_json},\"mount_count\":${mount_count_json}}"
json_line="{\"schema\":\"agent_hook_execution\",\"hook_name\":\"${escaped_hook_name}\",\"hook_version\":\"${escaped_hook_version}\",\"client\":\"${escaped_client}\",\"event_type\":\"${escaped_event_type}\",\"decision\":\"${decision}\",\"deny_reason\":${deny_reason_json},\"duration_bucket\":\"${duration_bucket}\",\"mode\":${mode_json},\"mount_count\":${mount_count_json}${ts_field}}"

write_telemetry_event "$json_line"
}
Expand All @@ -161,8 +175,11 @@ write_install_event() {
escaped_hook_name=$(escape_json_string "$hook_name")
escaped_hook_version=$(escape_json_string "$hook_version")

local ts_field
ts_field=$(ts_json_fragment)

local json_line
json_line="{\"schema\":\"agent_hook_install\",\"client\":\"${escaped_client}\",\"hook_name\":\"${escaped_hook_name}\",\"hook_version\":\"${escaped_hook_version}\",\"install_method\":\"${install_method}\"}"
json_line="{\"schema\":\"agent_hook_install\",\"client\":\"${escaped_client}\",\"hook_name\":\"${escaped_hook_name}\",\"hook_version\":\"${escaped_hook_version}\",\"install_method\":\"${install_method}\"${ts_field}}"

write_telemetry_event "$json_line"
}
Expand Down
13 changes: 12 additions & 1 deletion tests/lib/telemetry.bats
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ create_consent_signal() {
# Create a file just over 1MB
dd if=/dev/zero of="$event_file" bs=1048577 count=1 2>/dev/null
write_telemetry_event '{"should":"not appear"}'
# File should still be ~1MB, not have our line appended
# File is at the cap, so the new line is skipped (drop-newest) — never
# rewritten — leaving the existing contents untouched.
! grep -q "should" "$event_file"
}

Expand Down Expand Up @@ -168,6 +169,10 @@ create_consent_signal() {
[[ "$line" != *'"duration_ms"'* ]]
[[ "$line" == *'"mode":"configured"'* ]]
[[ "$line" == *'"mount_count":3'* ]]
# ts stamps when the hook fired; OPH maps it to the Snowplow device-created
# timestamp. Must be an unquoted integer so it parses as a JSON number.
[[ "$line" =~ \"ts\":[0-9]+ ]]
[[ "$line" != *'"ts":"'* ]]
}

@test "write_execution_event: deny_reason is set when provided" {
Expand Down Expand Up @@ -279,6 +284,12 @@ create_consent_signal() {
[[ "$line" == *'"hook_name":"validate_mounted_env_files"'* ]]
[[ "$line" == *'"hook_version":"1.0.0"'* ]]
[[ "$line" == *'"install_method":"install_script"'* ]]
[[ "$line" =~ \"ts\":[0-9]+ ]]
[[ "$line" != *'"ts":"'* ]]
# JSON must be parseable with the ts field present.
if command -v python3 >/dev/null 2>&1; then
echo "$line" | python3 -c 'import sys, json; json.loads(sys.stdin.read())'
fi
}

# ========== emit_manual_install_event_once ==========
Expand Down
Loading