MddlZddlZddlZddlZddlmZddlmZddlm Z ddl m Z m Z m Z ddlZddlZddlmZddlmZejeZdZGd d e ZGd d eZGd dZde ede efdZdedefdZdededefdZGddZ Gdde Z!Gdde!Z"Gdd e!Z#dS)!N)abstractmethod)suppress)dedent)MappingOptionalProtocol)atomic_rewrite)open_dir_no_symlinksic~eZdZe d dedefdZededdfd Zed ee defd Z dS) IConfigProviderFT force_read ignore_errorsctNNotImplementedError)selfr rs ^/opt/imunify360/venv/lib/python3.11/site-packages/defence360agent/contracts/config_provider.pyread_config_filez IConfigProvider.read_config_files "!configreturnNctrr)rrs rwrite_config_filez!IConfigProvider.write_config_file!!r timestampctrrrrs rmodified_sincezIConfigProvider.modified_since!rrFT) __name__ __module__ __qualname__rboolrrrrfloatrrrr r s>B"""7;"""^" ""D"""^"""D"""^"""rr ceZdZdS) ConfigErrorN)r!r"r#r&rrr(r(&sDrr(ceZdZdZdZdZdS) JsonMessagezPretty-print given *obj* as JSON. To be used for logging. Example: logging.info("object: %s", JsonMessage(obj)) c||_dSr)_obj)robjs r__init__zJsonMessage.__init__3s  rc8tj|jdS)NT) sort_keys)jsondumpsr,rs r__str__zJsonMessage.__str__6sz$)t4444rN)r!r"r#__doc__r.r4r&rrr*r**s<55555rr* prev_sectionsectionc^pipiz }z }fd|Dfd|DfdzDdS)z*Return difference between config sections.c"i|] }|| Sr&r&).0vr6s r z diff_section..As ; ; ;Qaa ; ; ;rc"i|] }|| Sr&r&)r:r;r7s rr<z diff_section..Bs 4 4 4a 4 4 4rcVi|]%}||k|||f&Sr&r&)r:r;r6r7s rr<z diff_section..DsE   A'!*,,  Q,,,,r)-+?keys)r6r7removed_settingsadded_settingss`` r diff_sectionrF:s%2LmG#((**W\\^^;\\^^l&7&7&9&99N ; ; ; ;*: ; ; ; 4 4 4 4^ 4 4 4     "''))GLLNN:     r prev_confconfc#bKz }fd|DVz }fd|DVfdzDVdS)z,Compare *prev_conf* with the current *conf*.c"i|] }|| Sr&r&)r:r7rGs rr<zdiff_config..Os G G G77Ig& G G Grc"i|] }|| Sr&r&)r:r7rHs rr<zdiff_config..Qs @ @ @g7DM @ @ @rcni|]1}||k|t||2Sr&)rF)r:r7rHrGs rr<zdiff_config..SsL     W g . . i0$w-@@ . . .rNrB)rGrHremoved_sectionsadded_sectionss`` r diff_configrOLs ~~''$))++5 G G G G6F G G GGGGYY[[9>>#3#33N @ @ @ @ @ @ @@@@     !((499;;6   r main_conf base_confrct||\}}}i}|D]\}}||vr|||<||vr||i||d||id||dD|S)a Return dict derived from *main_conf* excluding parts that are equal in *base_conf*. For example, >>> base_conf = { "SECTION1": {"OPTION1": "default", "OPTION2": "default"}, "SECTION2": {"OPTION1": "default"} } >>> main_conf = { "SECTION1": {"OPTION1": "value", "OPTION2": "default"}, "SECTION2": {"OPTION1": "default"} } >>> >>> exclude_equals(main_conf=main_conf, base_conf=base_conf) {'SECTION1': {'OPTION1': 'value'}} >>> r@c&i|]\}}||dS)r&)r:kr;s rr<z"exclude_equals..ts"CCCTQAaDCCCrrA)rOitemsrC setdefaultupdate)rPrQ_addedchangedresultr7values rexclude_equalsr^Zs$$Iy99Aug F#//++ ejjll " "#F7O gllnn $ $   gr * * 1 1''2B32G H H H   gr * * 1 1CCWW%5c%:%@%@%B%BCCC    MrceZdZdZddZdZdZ dd ed ed efd Z d e d efdZ dZ dZ d e fdZd e fdZdeed efdZdS) ConfigReaderzM ConfigFile file for settings page. Location config file is PATH Nc0||_||_||_dSr)path disclaimer permissions)rrcrdres rr.zConfigReader.__init__s $&rcNd|jj|jS)Nz<{classname}({path})>) classnamerc)format __class__r#rcr3s r__repr__zConfigReader.__repr__s+&--n1 .   rcd|jS)NzConfigReader at )rcr3s rr4zConfigReader.__str__s-$)---rFTr rrc2 tj|jtkrt d|j}t |d5}t d||}dddn #1swxYwYn/#t$r}t d|d}~wt$ricYSwxYw | |S#t$r*}t ||ricYd}~S|d}~wwxYw)zCRead config file into memory. Raises ConfigError. zConfig file is too largerzReading config file %sNzUnable to decode config file) osrcgetsize_MAX_CONFIG_SIZEr(openloggerinforeadUnicodeDecodeErrorFileNotFoundErrorload_config_bodyerror)rr rfilename config_filetextes rrzConfigReader.read_config_filesu wty)),<<<!"<===yHh$$ *  4h???"'')) * * * * * * * * * * * * * * *" E E E<==1 D    III  ((.. .    LLOOO  G  sfAB0B BBBBB C 'B77C C  C"" D,D DDDr{c  tj|}n+#tj$r}td|d|d}~wwxYw|iSt |t s(td|j||S)Nz.Imunify360 config is not valid YAML document ()z;Imunify360 config is invalid or empty: path={!r}, text={!r})yaml safe_load YAMLErrorr( isinstancedictrhrc)rr{rr|s rrwzConfigReader.load_config_bodys ^D))FF~   EEEE   >I&$'' ))/ 4)@)@   s?:?cdSrr&r3s r _pre_writezConfigReader._pre_write rcdSrr&r3s r _post_writezConfigReader._post_writerrcd}|jr|t|jz }|dz }|tj|dz }|S)Nra F)default_flow_style)rdrrdumprr config_texts r_serialize_configzConfigReader._serialize_configsN ? 6$/22 2K 4 KtyEBBBB rc|||}t|j|d|j||S)NF)backupre)rrr rcrerrs rrzConfigReader.write_config_filesd ,,V44  I{5d>N     rrcdS)NTr&rs rrzConfigReader.modified_sincestrraNr )r!r"r#r5r.rjr4r$rrstrrwrrrrrr%rr&rrr`r`ys# ''''    ...?C7; 4ST&      33Drr`cbeZdZdfd ZdZ ddedeffd Zdd Zd ee d efd Z xZ S)CachedConfigReaderraNct||d|_d|_i|_||_dSr)superr.mtimesize_configrerrcrdreris rr.zCachedConfigReader.__init__s@ z***&* %)  &rcfd|jj|j|j|jS)Nz9{classname} <'{path}', modified at {mtime}, {size} bytes>)rgrcrr)rhrir#rcrrr3s rr4zCachedConfigReader.__str__s7 G N N.5YjY O   rFTr rcH||js|r|j} t||_|jWt t ||j}t|r&tj d|gtt|Rn]#t$rP}tj|td|t|j|s|Yd}~nd}~wwxYw||jS)z(Update config if config file is modified)rNz-%s modified: removed=%s, added=%s, changed=%sz*%s is invalid, using previous settings: %s)rrrrrlistrOanyrrrsmapr*r( sentry_sdkcapture_exceptionwarning_refresh_stat_cache)rr r prev_configdiffsrxris rrz#CachedConfigReader.read_config_filesW   tz * * 'j ',K $ww77"/ 8   :) [$,!G!GHHE5zz K !e44 ,U333@ -- % K      *  $ $ & & &|s'B,, D6ADDrc tj|j}|j|_|j|_dS#t$rd|_d|_YdSwxYw)z-Sync cached mtime/size with the file on disk.N)rnstatrcst_mtimerst_sizerrv)rrs rrz&CachedConfigReader._refresh_stat_cache s] 749%%DDJ DIII    DJDIIII s15AArc|d} tj|j}|j|j}}n#t $rd\}}YnwxYw||kp ||jkS)zWhether the config has updated since *timestamp*. (as defined by its last modification time and size) :param timestamp: None means that the file has never been read before Nr)rr)rnrrcrrrvr)rrrrrs rrz!CachedConfigReader.modified_sincesz  I <749%%D!% t|gHH! ) ) ) ( Hggg ))#;w$)';;s.AArr )rN) r!r"r#r.r4r$rrrr%r __classcell__ris@rrrs''''''   ?C!!!7;!!!!!!F<//`` and the config file inside it must end up owned by ``root:`` with modes ``0750`` / ``0640``. Earlier revisions performed the ``mkdir`` -> ``chown`` -> ``chmod`` sequence on path strings, which left a TOCTOU window: between the directory existing and the metadata syscalls, a swap to a symlink could redirect the chown to an arbitrary inode. See DEF-41586 / CLOS-3965 for context. The hardened path opens the parent ``USER_CONFDIR`` once with ``O_NOFOLLOW`` at every component, then performs every subsequent operation (``mkdir``/``chown``/``chmod``/atomic write) relative to that fd or to a fresh ``O_NOFOLLOW`` fd of the user subdir. No user-controlled path string is dereferenced more than once. iicXt|||_dSr)rr.username)rrcrris rr.zUserConfigReader.__init__Ms&   rcd|jS)NzConfig of user )rr3s rr4zUserConfigReader.__str__Qs0000r parent_fdnamerctt5tj||j|dddn #1swxYwYtj|tjtjztjz|S)aReturn an O_NOFOLLOW fd for ``name`` inside *parent_fd*. Creates the directory first if it does not already exist. The ``O_NOFOLLOW`` flag guarantees that, if a symlink appears in the slot at any time after this call returns, every subsequent ``fchown``/``fchmod``/atomic-rewrite bound to the returned fd operates on the originally opened inode. )modedir_fdNr) rFileExistsErrorrnmkdirDIR_PERMISSIONSrqO_RDONLY O_DIRECTORY O_NOFOLLOW)rrrs r_open_user_subdirz"UserConfigReader._open_user_subdirTso & & H H HT 4Y G G G G H H H H H H H H H H H H H H Hw  K". (2= 8    s>AAc tj|jj}tj|j\}}tj|\}}t|} |||} t j |d|t j ||j | |} t|| dd||j|t j|tjtjz|} t j | d|t j | |jt j| n#t j| wxYw t j|n#t j|wxYw t j|n#t j|wxYw| S)NrF)ruidgidrerr)pwdgetpwnamrpw_gidrnrcsplitr rchownfchmodrrr FILE_PERMISSIONSrqrrclose) rrrconfdirbasename userconfdirrruser_fdrfile_fds rrz"UserConfigReader.write_config_fileesl4=))0GMM$)44 " g 6 6 X )55 * ,,YAAG& "!S))) '4#7888"44V<<   $ 5"'K"-/" &HWa---Igt'<===HW%%%%BHW%%%%%!!!!!!!!! HY    BHY    s=8GBF0E#F#E99F=GF((GG) r!r"r#r5rrr.r4intrrrrrs@rrr8s"O!!!!!111 3 c c    "6366666666rr)$r1loggingrnrabcr contextlibrtextwraprtypingrrrrrdefence360agent.utilsr defence360agent.utils.fd_opsr getLoggerr!rrrpr Exceptionr(r*rrFrOr^r`rrrr&rrrs  .......... 000000======  8 $ $ " " " " "h " " "      )    5 5 5 5 5 5 5 5 x~$ 4 t    $4>XXXXXXXXvN<N<N<N<N<N<N<N