/ 0UdZddlmZddlZddlZddlZddlmZdZdZ ia de d<d a d e d <d"dZ d#dZd$dZd%dZd&dZd%dZd'dZd'dZd(d)d!ZdS)*a Shared reader for the local feature flags file. The file is written by: - Go resident-agent FeatureFlags plugin (IM360 mode) - Python FeatureFlagsSync plugin (AV mode) Other subsystems (e.g. message_status_publisher) use this module to check individual flag values at runtime. Supported JSON shapes on disk (readers / ``is_enabled``): - Legacy object ``{"mqtt_tracking": true, ...}`` (preferred on disk after sync). - JSON array of enabled names ``["mqtt_tracking"]`` (still accepted). - Legacy wrapper ``{"flags": ["mqtt_tracking", ...]}``. The sync API checksum is always over a **canonical JSON array** of enabled names (same as correlation sync). ``sync_checksum_hex_from_flags_file`` derives that MD5 from whatever shape is on disk. The sync plugin also writes ``FLAGS_PLAIN_PATH`` (``/var/imunify360/feature_flags``): plain text, one enabled flag name per line (sorted), for scripts. ) annotationsN)Anyz"/var/imunify360/feature_flags.jsonz/var/imunify360/feature_flagsdict[str, Any] _cached_flagsfloat _cached_mtimerawrreturnc|iSt|tr#i}|D]}t|trd||<|St|tr;|d}t|trt |S|SiS)z@Map file JSON to a flat name->value dict for :func:`is_enabled`.NTflags) isinstanceliststrdictget_normalize_flags_from_file)r outiteminners \/opt/imunify360/venv/lib/python3.11/site-packages/defence360agent/internals/feature_flags.pyrr's { #t  ! !D$$$ ! D  #t   eT " " 5-e44 4 Ic tjt}n#t$riadat cYSwxYw|t krt S tt5}tj |}dddn #1swxYwYt|an#ttj f$riaYnwxYw|at S)Nr) ospathgetmtime FLAGS_PATHOSErrorrr openjsonloadrJSONDecodeError)mtimefr s r _read_flagsr%9s  ,,      *   )A,,C               2377 T) * M sB$'??B%)B > B% BB%BB%%C?Cr list[str]ct|ttfs$tdt |jt |}td|DS)aReturn sorted enabled flag names for JSON and plain-text sidecar. Accepts the same shapes as :func:`_normalize_flags_from_file` (array, flat map, ``{"flags": [...]}``) so checksums and sidecars match Go ``enabledNamesSortedForChecksum`` / :func:`is_enabled`. z flags must be list or dict, not c3$K|] \}}||V dS)N).0kvs r z,enabled_flag_names_sorted..[s+881a8!888888r) rrr TypeErrortype__name__rsorteditems)r normalizeds renabled_flag_names_sortedr4Osx edD\ * *  EtE{{/C E E   ,E22J 88 0 0 2 2888 8 88rnamesbytescrt|}tj|ddS)zYJSON array bytes used for sync MD5 (matches correlation_api checksum_for_sync_flag_list).T sort_keysindentr1r dumpsencode)r5ordereds rcanonical_sync_flag_list_bytesr@^s0UmmG :ga 8 8 8 ? ? A AArrrcR t|d5}tj|}dddn #1swxYwYn##tttjf$rYdSwxYwt |}t|}tj |d S)zOMD5 hex of canonical enabled-name array for ``path``, or "" if missing/invalid.zutf-8)encodingNF)usedforsecurity) rr r!rUnicodeDecodeErrorr"r4r@hashlibmd5 hexdigest)rr$r r5payloads r!sync_checksum_hex_from_flags_filerJds $ ) ) ) Q)A,,C                ')= >rr %c * *E,U33G ;w 6 6 6 @ @ B BBs,A4 A8A8AA A cdtd|DD}tj|ddS)zz2legacy_feature_flags_map_bytes..rsKKKQDKKKrc<h|]}t|t|Sr))rr)r*xs r z1legacy_feature_flags_map_bytes..rs'!I!I!IjC6H6H!I!!I!I!IrTr8r9r<)r5ds rlegacy_feature_flags_map_bytesrSpsOKK&!I!IU!I!I!IJJKKKA :a4 2 2 2 9 9 ; ;;rc|t|}|sdSd|dzS)zPBody for ``FLAGS_PLAIN_PATH``: one name per line, trailing newline if non-empty.r )r4joinr>)r r5s r$plain_text_payload_for_enabled_flagsrWvs? %e , ,E s IIe  t # + + - --rct|tr)tj|ddSt dt |j)zBSerialize dict flags for writing ``FLAGS_PATH`` (legacy map only).Tr8r9zflags must be dict, not )rrr r=r>r.r/r0)r s r$serialize_feature_flags_file_payloadrY~sX%Dz%4:::AACCC EtE{{/CEE F FFrF flag_namedefaultboolcnt}||}||St|S)zReturn whether *flag_name* is enabled. If the file is missing, unreadable, or the flag is absent, *default* is returned. Defaults to False so unknown flags are treated as disabled unless the caller explicitly opts in. )r%rr\)rZr[r values r is_enabledr_s4 MME IIi E } ;;r)r rr r)r r)r rr r&)r5r&r r6)rrr r)r rr r6)F)rZrr[r\r r\)__doc__ __future__rrFr rtypingrrFLAGS_PLAIN_PATHr__annotations__r rr%r4r@rJrSrWrYr_r)rrresQ.#"""""  1 2 " """" $, 9 9 9 9BBBB C C C C<<<< ....GGGG       r