Skip to content
Open
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
29 changes: 19 additions & 10 deletions flame/utils/mock_flame_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
from io import StringIO
from typing import Any, Literal, Optional, Union

from opendp.mod import enable_features
from opendp.domains import atom_domain
from opendp.measurements import make_laplace
from opendp.metrics import absolute_distance
try:
from opendp.mod import enable_features
from opendp.domains import atom_domain
from opendp.measurements import make_laplace
from opendp.metrics import absolute_distance
USE_OPENDP = True
except ImportError:
USE_OPENDP = False
Comment on lines +12 to +13
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="flame/utils/mock_flame_core.py"
if [ ! -f "$FILE" ]; then
  echo "Missing file: $FILE"
  exit 1
fi

echo "=== Lines 1-80 ==="
sed -n '1,80p' "$FILE" | nl -ba -w2 -s':'

echo "=== Lines 240-320 ==="
sed -n '240,320p' "$FILE" | nl -ba -w2 -s':'

echo "=== Search for USE_OPENDP ==="
rg -n "USE_OPENDP" "$FILE" || true

echo "=== Search for make_laplace / local_dp usage ==="
rg -n "make_laplace|local_dp|sensitivity|epsilon|absolute_distance|ZeroDivisionError|KeyError" "$FILE" || true

Repository: PrivateAIM/python-sdk-patterns

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="flame/utils/mock_flame_core.py"

echo "=== Existence check ==="
ls -la "$FILE" || (echo "Missing: $FILE"; exit 1)

echo "=== Lines 1-120 ==="
sed -n '1,120p' "$FILE" | cat -n -ba

echo "=== Lines 240-340 ==="
sed -n '240,340p' "$FILE" | cat -n -ba

echo "=== Search for USE_OPENDP ==="
rg -n "USE_OPENDP" "$FILE" || true

echo "=== Search for DP/scale logic ==="
rg -n "make_laplace|local_dp|sensitivity|epsilon|absolute_distance" "$FILE" || true

Repository: PrivateAIM/python-sdk-patterns

Length of output: 283


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="flame/utils/mock_flame_core.py"

echo "=== OpenDP import block (lines 1-80) ==="
awk '{printf "%4d:%s\n", NR, $0}' "$FILE" | sed -n '1,80p'

echo "=== USE_OPENDP occurrences ==="
rg -n "USE_OPENDP" "$FILE" || true

echo "=== DP/scale logic region (lines 240-340) ==="
awk '{printf "%4d:%s\n", NR, $0}' "$FILE" | sed -n '240,340p'

echo "=== Search for local_dp and make_laplace usage ==="
rg -n "local_dp|make_laplace|sensitivity|epsilon" "$FILE" || true

Repository: PrivateAIM/python-sdk-patterns

Length of output: 9773


Narrow the OpenDP fallback to “opendp missing” and harden local DP scale calculation

  • The OpenDP import uses except ImportError: USE_OPENDP = False, which can silently disable DP and trigger the misleading “mockdp dependency group is not installed” warning even when opendp is present but incompatible; catch only the case where the top-level opendp package is missing (e.g., ModuleNotFoundError with e.name == "opendp"), and re-raise all other import failures.
  • The local DP Laplace scale is computed as local_dp['sensitivity'] / local_dp['epsilon'] with no validation; missing keys or epsilon == 0 can raise KeyError/ZeroDivisionError during result submission—validate inputs (and log/skip/raise) before computing scale.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flame/utils/mock_flame_core.py` around lines 12 - 13, Change the broad
ImportError handler so only a missing top-level opendp disables the feature:
catch ModuleNotFoundError and check e.name == "opendp" before setting USE_OPENDP
= False, and re-raise any other import exceptions; additionally harden the local
DP scale calculation that uses local_dp['sensitivity'] / local_dp['epsilon'] by
validating local_dp contains the 'sensitivity' and 'epsilon' keys and that
epsilon is non-zero (raise or log and skip the DP submission on invalid input)
where the scale is computed, referencing the USE_OPENDP symbol and the local_dp
dict/scale computation to locate the changes.



from flamesdk.resources.utils.constants import LogTypeLiteral

Expand Down Expand Up @@ -261,12 +266,16 @@ def submit_final_result(self,
if self.get_id() == self.get_aggregator_id():
if local_dp is not None:
if type(result) in [int, float]:
enable_features("contrib")
scale = local_dp['sensitivity'] / local_dp['epsilon'] # Laplace scale parameter
laplace_mech = make_laplace(input_domain=atom_domain(T=float),
input_metric=absolute_distance(T=float),
scale=scale)
result = laplace_mech(float(result))
if USE_OPENDP:
enable_features("contrib")
scale = local_dp['sensitivity'] / local_dp['epsilon'] # Laplace scale parameter
laplace_mech = make_laplace(input_domain=atom_domain(T=float),
input_metric=absolute_distance(T=float),
scale=scale)
result = laplace_mech(float(result))
Comment on lines +271 to +275
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate local_dp before computing Laplace scale.

Line 271 can raise KeyError or ZeroDivisionError (epsilon missing/zero), which aborts submit_final_result.

Proposed fix
                     if USE_OPENDP:
                         enable_features("contrib")
-                        scale = local_dp['sensitivity'] / local_dp['epsilon']  # Laplace scale parameter
+                        sensitivity = local_dp.get("sensitivity")
+                        epsilon = local_dp.get("epsilon")
+                        if sensitivity is None or epsilon in (None, 0):
+                            self.flame_log(
+                                "Invalid local_dp config. Expected non-zero 'epsilon' and provided 'sensitivity'. DP step will be skipped.",
+                                log_type=LogTypeLiteral.WARNING.value
+                            )
+                            self.final_results_storage = result
+                            self.__pop_logs__()
+                            return {"result": "submitted"}
+                        scale = sensitivity / epsilon  # Laplace scale parameter
                         laplace_mech = make_laplace(input_domain=atom_domain(T=float),
                                                     input_metric=absolute_distance(T=float),
                                                     scale=scale)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flame/utils/mock_flame_core.py` around lines 271 - 275, Validate local_dp
before computing the Laplace scale: in the code path that computes scale =
local_dp['sensitivity'] / local_dp['epsilon'] (used when creating laplace_mech
and applying it to result), first check that 'sensitivity' and 'epsilon' keys
exist in local_dp and that epsilon is a non-zero positive number; if validation
fails, handle it deterministically (e.g., raise a clear ValueError or log and
abort/skip DP noise) so KeyError/ZeroDivisionError cannot propagate out of
submit_final_result / the Laplace noise application. Ensure the check occurs
right before computing scale and that the error handling path provides a clear
message including which key is invalid.

else:
self.flame_log("The 'mockdp' dependency group is not installed. Local DP will not be applied to the result.",
log_type=LogTypeLiteral.WARNING.value)
else:
self.flame_log("Given result type is not supported for local DP -> DP step will be skipped.",
log_type=LogTypeLiteral.WARNING.value)
Expand Down
Loading