Ë
    ÛJêjËY  ã                  óØ  — U d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZ ddlmZmZ ddlmZ ddlmZ g d	¢Zd
Zded<   dZded<   dddddœZded<    eddh«      Zded<    ej4                  d«      Zded<   dZded<    ej4                  d «      Zded!<    G d"„ d#e«      Zd/d$„Zd0d%„Z d1d&„Z!d2d'„Z"d3d(„Z# ed)¬*«       G d+„ d,«      «       Z$ ed)¬*«       G d-„ d.«      «       Z%y)4u…  Engagement scope and owner authorization for the pentest agent.

This module owns the two pieces of state a penetration test needs and the
current policy language cannot express:

* :class:`TargetScope` â€” the whitelist of hosts/CIDRs a test may touch, and
* :class:`EngagementAuthorization` â€” the artifact a human signed off on.

Why this is code and not a policy rule
--------------------------------------
The policy DSL evaluates a boolean expression over an AST allowlist
(``security_plane/policy/__init__.py``); a condition has to be a bool no longer
than 256 characters and may only call ``is_sensitive``. "Is this address inside
the approved range" is a function call returning a bool, so it *looks* expressible
â€” but writing it would mean either giving up that allowlist or smuggling the
range into a string. Both are worse than a typed object with its own tests, so
the whitelist is a value with a real parser, and it is consulted on **every**
tool invocation rather than once per session.

Scope decisions that are security properties, not conveniences
-------------------------------------------------------------
* An empty scope authorises nothing. ``TargetScope`` with no networks and no
  hosts refuses every target; there is no "no scope configured means allow".
* A target may only be a single host, a ``host:port`` pair, or an ``http(s)``
  URL. Credentials in the authority (``http://user:pw@host``), control
  characters, whitespace and shell metacharacters are refused outright, because
  the target text ends up in a process argument vector.
* A CIDR mask **in a tool target** is accepted only when the resulting network
  is a subnet of an authorised network. Checking only the address would let
  ``10.0.0.0/8`` through a scope of ``10.0.0.0/24``, which is the obvious way
  to bypass a whitelist and the reason the mask is validated, not stripped.
* Host *names* are accepted only when the name itself was listed in the
  engagement. Names are not resolved during authorization: resolving here would
  make the check depend on DNS the caller controls (rebinding between the check
  and the run), so a name is either explicitly authorised or refused.
é    )ÚannotationsN)ÚIterable)Ú	dataclass)ÚAnyÚFinal)Úurlsplit)ÚSecurityError)ÚMAX_ENGAGEMENT_LEVELÚTOOL_CLASSESÚTOOL_CLASS_BY_LEVELÚEngagementAuthorizationÚTargetScopeÚTargetScopeErroré   z
Final[int]r
   )ÚpassiveÚreconÚvulnerabilityÚexploitationzFinal[tuple[str, ...]]r   r   r   r   r   )r   é   é   r   zFinal[dict[int, str]]r   ÚhttpÚhttpszFinal[frozenset[str]]Ú_ALLOWED_URL_SCHEMESz'^[a-z0-9]([a-z0-9._-]{0,251}[a-z0-9])?$zFinal[re.Pattern[str]]Ú_HOSTNAME_REéÿ   Ú_MAX_TARGET_LENGTHz^0[xX][0-9a-fA-F]+$Ú_HEX_ONLY_REc                  ó   — e Zd ZdZy)r   zþA target is unusable or lies outside the authorised engagement scope.

    Inherits from ``SecurityError`` so a caller can refuse on the same signal the
    rest of the security plane uses; every instance is a refusal, never a
    recoverable error.
    N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© ó    úD/home/oem/CyberBrain26-unified/src/cyberbrain26/agents/engagement.pyr   r   V   s   „ òr$   r   c                óN   — 	 t        j                  | «       y# t        $ r Y yw xY w)NFT)Ú	ipaddressÚ
ip_addressÚ
ValueError©Úhosts    r%   Ú_is_ip_literalr,   _   s.   € ðÜ×Ñ˜TÔ"ð øô ò Ùðús   ‚ ˜	$£$c                ó^  — | j                  «       }|j                  d«      rR|j                  d«      }|dk(  rt        d| ›�«      ‚|d| }||dz   d }|r~|j                  d«      smt        d| ›�«      ‚|j	                  d«      dk(  rI|j                  d«      \  }}}|j                  «       rdt        |«      cxk  rd	k  sn t        d
|›�«      ‚|}|j                  «       j                  «       }|st        d«      ‚t        |«      s$t        j                  |«      st        d|›d�«      ‚|S )zFReturn the bare host of an authority component, refusing anything odd.ú[ú]éÿÿÿÿz"malformed IPv6 literal in target: r   Nú:r   i   z)target port is not a valid TCP/UDP port: ztarget carries no host partztarget host z) is neither an IP literal nor a host name)ÚstripÚ
startswithÚfindr   ÚcountÚ	partitionÚisdigitÚintÚlowerr,   r   Úmatch)ÚnetlocÚvalueÚendr+   Ú	remainderÚ_Úports          r%   Ú_host_from_netlocrA   g   s*  € à�L‰L‹N€EØ×Ñ˜ÔØ�j‰j˜‹oˆØ�"Š9Ü"Ð%GÈÀzÐ#RÓSÐSØ�Q�sˆ|ˆØ˜# ™'˜)Ð$ˆ	Ù˜Y×1Ñ1°#Ô6Ü"Ð%GÈÀzÐ#RÓSÐSØ	�‰�SÓ	˜QÒ	ØŸ™¨Ó,‰ˆˆa�Ø�|‰|Œ~ Q¬¨T«Ô%:°UÔ%:Ü"Ð%NÈtÈhÐ#WÓXÐXð ˆØ�:‰:‹<×ÑÓ€DÙÜÐ<Ó=Ð=Ü˜$Ô¬×(:Ñ(:¸4Ô(@Ü ¨d¨XÐ5^Ð_Ó`Ð`Ø€Kr$   c                ó   — d| v ryd| v rd| j                  d«      }|dk(  rt        d| ›�«      ‚| |dz   d }|dk(  s2|j                  d	«      r|dd j                  «       st        d
| ›�«      ‚yd| vry| j	                  d«      \  }}}|r|j                  «       st        d| ›�«      ‚t        |«      }d|cxk  rdk  sn t        d| ›�«      ‚|S )aÓ  Return the CIDR prefix length a target declares, or ``None``.

    A ``/`` is only meaningful on the network part of a literal address. A URL
    path (``http://host/x``) is not a mask, so the split happens on the raw text
    only when the text is not a URL.

    A bracketed IPv6 authority (``[2001:db8::1]:443``) is not a mask either, and
    returns ``None``. Anything after the closing bracket other than a port is
    refused outright rather than ignored: dropping the tail would silently turn
    ``[2001:db8::1]/64`` into a bare address, and a single-address engagement
    would then authorise 2**64 addresses to a tool that reads the same text as
    ``addr/prefix``. Refusing keeps "one string, one parser".
    ú://Nr.   r/   r0   zunbalanced bracket in target: r   Ú r1   z?target must not carry anything after a bracketed IPv6 literal: ú/zmalformed CIDR in target: r   é€   z$CIDR prefix out of range in target: )r4   r   r3   r7   r6   r8   )ÚtargetÚclosingÚtailÚaddress_partr?   Úprefix_textÚprefixs          r%   Ú
_prefix_ofrM   �   s   € ð ��ØØ
ˆf�}Ø—+‘+˜cÓ"ˆØ�bŠ=Ü"Ð%CÀFÀ:Ð#NÓOÐOØ�g ‘k�mÐ$ˆØ˜’
˜tŸ™¨sÔ3¸¸Q¸R¸×8HÑ8HÔ8JÜ"ØQÐRXÐQ[Ð\óð ð Ø
�&ÑØØ#)×#3Ñ#3°CÓ#8Ñ €L�!�[Ù˜{×2Ñ2Ô4ÜÐ!;¸F¸:ÐFÓGÐGÜ�Ó€FØ�Ô˜#ÔÜÐ!EÀfÀZÐPÓQÐQØ€Mr$   c                ó¦  — t        | t        «      st        d«      ‚| j                  «       }|st        d«      ‚t	        |«      t
        kD  rt        dt
        › d�«      ‚t        d„ |D «       «      rt        d«      ‚d|v rŒ	 t        |«      }|j                  j                  «       t        vr)t        d
|j                  ›dt        t        «      › d�«      ‚d|j                  v rt        d«      ‚t        t        |j                  «      «      S |j!                  d«      d   }t        t        |«      «      S # t        $ r}t        d|›�«      |‚d	}~ww xY w)u
  Return the host of ``target``, refusing anything that is not a plain host.

    Accepts ``host``, ``host:port``, ``[v6]:port``, ``10.0.0.0/24`` and
    ``http(s)://host[:port]/path``. Everything else â€” a non-string, an empty
    string, embedded whitespace or control characters, an unsupported URL
    scheme, credentials in the authority â€” is a :class:`TargetScopeError`,
    because each of those is a way to make the text in the argument vector mean
    something other than the address the reviewer approved.
    ztarget must be a stringztarget must not be emptyztarget is longer than z charactersc              3  ó\   K  — | ]$  }|j                  «       xs t        |«      d k  –— Œ& y­w)é    N)ÚisspaceÚord)Ú.0Ú	characters     r%   ú	<genexpr>z&extract_target_host.<locals>.<genexpr>·   s*   è ø€ Ò
N¸Iˆ9×ÑÓÒ7¤# i£.°4Ñ"7Ó7Ñ
Nùs   ‚*,z8target must not contain whitespace or control charactersrC   z!target is not a well-formed URL: Nztarget URL scheme z is not allowed (allowed: ú)ú@z2target must not carry credentials in its authorityrE   r   )Ú
isinstanceÚstrr   r2   Úlenr   Úanyr   r)   Úschemer9   r   Úsortedr;   Ú_reject_numeric_hostrA   r6   )rG   ÚtextÚpartsÚexcrJ   s        r%   Úextract_target_hostrb   ¦   sP  € ô �fœcÔ"ÜÐ8Ó9Ð9Ø�<‰<‹>€DÙÜÐ9Ó:Ð:Ü
ˆ4ƒyÔ%Ò%ÜÐ!7Ô8JÐ7KÈ;ÐWÓXÐXÜ
Ñ
NÈÔ
NÔNÜÐYÓZÐZà��}ð	ZÜ˜T“NˆEð �<‰<×ÑÓÔ';Ñ;Ü"Ø$ U§\¡\Ð$4ð 5Ü#Ô$8Ó9Ð:¸!ð=óð ð �%—,‘,ÑÜ"Ð#WÓXÐXÜ#Ô$5°e·l±lÓ$CÓDÐDð —>‘> #Ó& qÑ)€LÜÔ 1°,Ó ?Ó@Ð@øô ò 	ZÜ"Ð%FÀtÀhÐ#OÓPÐVYÐYûð	Zús   Â D3 Ä3	EÄ<EÅEc                óp   — | r3| j                  «       st        j                  | «      rt        d| ›�«      ‚| S )a  Refuse a host part that is only digits or only hex digits.

    ``2130706433`` and ``0x7f000001`` are not host names and are not
    :func:`ipaddress.ip_address` literals either, so they survive every other
    check here and then reach the tool, which resolves them numerically - to
    loopback, in the case of the two examples above. Refusing them at the
    boundary keeps "the string we checked" and "the string the tool reads" the
    same string, instead of relying on the tool to agree with us about syntax.
    z0target host must not be a bare numeric address: )r7   r   Ú	fullmatchr   r*   s    r%   r^   r^   Ó   s:   € ñ �—‘”¤<×#9Ñ#9¸$Ô#?ÜØ>¸t¸hÐGó
ð 	
ð €Kr$   T)Úfrozenc                  óz   — e Zd ZU dZdZded<    e«       Zded<   dd„Ze	dd„«       Z
dd	„Zdd
„Zdd„Z	 	 	 	 dd„Zy)r   aK  Whitelist of networks and hosts a pentest tool call may address.

    Checked on every invocation from :func:`authorize_tool_call` in
    ``agents.pentest_catalog``; never once per session. Construct it from the
    signed engagement artifact with :meth:`from_targets` rather than by hand, so
    the whitelist and the artifact cannot disagree.

    Attributes:
        networks: Parsed CIDR networks. Empty means "no network target".
        hosts: Individually authorised hosts (literal IPs and names). An IP
            listed here does not imply its /24; only ``networks`` does.
    r#   ztuple[Any, ...]Únetworksúfrozenset[str]Úhostsc                óä  — g }| j                   D ]n  }t        |«      j                  «       }|st        d«      ‚	 t	        j
                  |d¬«      }|j                  dk(  rt        d|›�«      ‚|j                  |«       Œp t        j                  | dt        |«      «       t        j                  | dt        d	„ | j                  D «       «      «       y # t        $ r}t        d|›�«      |‚d }~ww xY w)
Nz0engagement scope contains an empty network entryF©Ústrictz"invalid CIDR in engagement scope: r   z.engagement scope must not be a default route: rg   ri   c              3  ój   K  — | ]+  }t        |«      j                  «       j                  «       –— Œ- y ­w©N©rY   r2   r9   )rS   r+   s     r%   rU   z,TargetScope.__post_init__.<locals>.<genexpr>  s$   è ø€ Ò$VÀ4¤S¨£Y§_¡_Ó%6×%<Ñ%<×%>Ñ$Vùó   ‚13)rg   rY   r2   r   r'   Ú
ip_networkr)   Ú	prefixlenÚappendÚobjectÚ__setattr__ÚtupleÚ	frozensetri   )ÚselfÚparsedÚrawr_   Únetworkra   s         r%   Ú__post_init__zTargetScope.__post_init__ö   sì   € ØˆØ—=‘=ò 	#ˆCÜ�s“8—>‘>Ó#ˆDÙÜ&Ð'YÓZÐZð_Ü#×.Ñ.¨t¸EÔB�ð × Ñ  AÒ%Ü&ØDÀTÀHÐMóð ð �M‰M˜'Õ"ð#	#ô$ 	×Ñ˜4 ¬U°6«]Ô;Ü×ÑØ�'œ9Ñ$VÈ4Ï:É:Ô$VÓVõ	
øô ò _Ü&Ð)KÈDÈ8Ð'TÓUÐ[^Ð^ûð_ús   ¹CÃ	C/ÃC*Ã*C/c                ó  — g }g }|D ]º  }t        |«      j                  «       }|st        d«      ‚t        |«      }t	        |«      }|€|j                  |«       ŒS	 t        j                  |› d|› �d¬«      }|j                  dk(  r|j                  |«       Œ‘|j                  |j                  › d|j                  › �«       Œ¼ |s|st        d«      ‚ | t        |«      t        |«      ¬	«      S # t        $ r}	t        d|›�«      |	‚d}	~	ww xY w)
zäBuild a scope from the target list an engagement was signed off on.

        A bare literal IP becomes a ``/32`` host entry rather than a network, so
        listing one address never widens into its surrounding prefix.
        z#engagement target must not be emptyNrE   Frk   z#invalid CIDR in engagement target: r   z2engagement scope is empty; it authorises no target)rg   ri   )rY   r2   r   rb   rM   rs   r'   rq   r)   Únum_addressesÚnetwork_addressrr   rv   rw   )
ÚclsÚtargetsrg   ri   rz   r_   r+   rL   r{   ra   s
             r%   Úfrom_targetszTargetScope.from_targets  s  € ð !ˆØˆØò 	RˆCÜ�s“8—>‘>Ó#ˆDÙÜ&Ð'LÓMÐMÜ& tÓ,ˆDÜ Ó%ˆFØˆ~Ø—‘˜TÔ"Øð`Ü#×.Ñ.°$°°q¸¸Ð/AÈ%ÔP�ð ×$Ñ$¨Ò)Ø—‘˜TÕ"à—‘ 7×#:Ñ#:Ð";¸1¸W×=NÑ=NÐ<OÐ PÕQð!	Rñ" ¡Ü"Ð#WÓXÐXÙœE (›O´9¸UÓ3CÔDÐDøô ò `Ü&Ð)LÈTÈHÐ'UÓVÐ\_Ð_ûð`ús   ÁC/Ã/	DÃ8DÄDc                óH   — t        | j                  xs | j                  «      S )z5Report whether this scope authorises anything at all.)Úboolrg   ri   ©rx   s    r%   Ú__bool__zTargetScope.__bool__-  s   € ä�D—M‘MÒ/ T§Z¡ZÓ0Ð0r$   c                óÈ   — |j                  «       j                  «       }|| j                  v ry	 t        j                  |«      }| j                  |«      S # t
        $ r Y yw xY w)z<Report whether a bare host (no port, no mask) is authorised.TF)r2   r9   ri   r'   r(   r)   Ú_network_contains)rx   r+   Ú	candidateÚaddresss       r%   Úallows_hostzTargetScope.allows_host1  sa   € à—J‘J“L×&Ñ&Ó(ˆ	Ø˜Ÿ
™
Ñ"Øð	Ü×*Ñ*¨9Ó5ˆGð ×%Ñ% gÓ.Ð.øô ò 	áð	ús   ¯A Á	A!Á A!c                ó  — 	 t        |«      }t        |t        «      rt        |«      nd}|€| j                  |«      S 	 t        j                  |› d|› �d¬«      }| j                  |«      S # t        $ r Y yw xY w# t        $ r Y yw xY w)a  Report whether ``target`` is inside this scope.

        Never raises: an unparseable target is simply not authorised, and the
        caller records the refusal. A target carrying a CIDR mask is authorised
        only when the whole network is a subnet of an authorised one.
        NFrE   rk   )
rb   rX   rY   rM   r   r‹   r'   rq   r)   rˆ   )rx   rG   r+   rL   r{   s        r%   ÚallowszTargetScope.allows=  sš   € ð	Ü& vÓ.ˆDÜ+5°f¼cÔ+B”Z Ô'ÈˆFð ˆ>Ø×#Ñ# DÓ)Ð)ð	Ü×*Ñ*¨d¨V°1°V°HÐ+=ÀeÔLˆGð ×%Ñ% gÓ.Ð.øô  ò 	Ùð	ûô ò 	Ùð	ús"   ‚(A+ ¾A: Á+	A7Á6A7Á:	BÂBc                óD  — | j                   D ]‘  }|j                  |j                  k7  rŒt        |t        j                  t        j
                  f«      r||v sŒL yt        |t        j                  t        j                  f«      r||k(  s|j                  |«      sŒ� y y y)NTF)	rg   ÚversionrX   r'   ÚIPv4AddressÚIPv6AddressÚIPv4NetworkÚIPv6NetworkÚ	subnet_of)rx   r‰   r{   s      r%   rˆ   zTargetScope._network_containsQ  s�   € ð —}‘}ò 	ˆGØ× Ñ  G§O¡OÒ3ØÜ˜)¤i×&;Ñ&;¼Y×=RÑ=RÐ%SÔTØ Ò'ÙÜ˜I¬	×(=Ñ(=¼y×?TÑ?TÐ'UÔVØ Ò'¨9×+>Ñ+>¸wÕ+GÙñ ð	ð r$   N©ÚreturnÚNone)r�   zIterable[object]r–   r   )r–   r„   ©r+   rY   r–   r„   )rG   rt   r–   r„   )r‰   z]ipaddress.IPv4Address | ipaddress.IPv6Address | ipaddress.IPv4Network | ipaddress.IPv6Networkr–   r„   )r   r    r!   r"   rg   Ú__annotations__rw   ri   r|   Úclassmethodr‚   r†   r‹   r�   rˆ   r#   r$   r%   r   r   ä   sb   … ñð !#€HˆoÓ"Ù%›K€Eˆ>Ó'ó
ð2 òEó ðEó:1ó
/ó/ð(ð ðð 
ôr$   r   c                  óÞ   — e Zd ZU dZded<   ded<   dZded<    ed	h«      Zd
ed<   dZded<   dZ	ded<   dZ
ded<   dZded<   dd„Zedd„«       Zdd„Zedd„«       Zd d!d„Zd"d„Zd#d„Zd"d„Zy)$r   a  The artifact a human authorised a pentest engagement under.

    Everything a reviewer needs in order to say yes or no is a field here, and
    :attr:`digest` is a stable fingerprint of all of it. The digest is what the
    operator re-states on the command line and what the audit trail records, so a
    run cannot quietly happen under different terms than the ones that were
    shown.

    Attributes:
        owner: Human or system accountable for the engagement.
        targets: The authorised target list; the scope is derived from it.
        max_level: Highest engagement level permitted (0-3).
        allowed_tool_classes: Tool classes this engagement may use. Must
            include the class belonging to ``max_level``.
        window_start: POSIX timestamp the engagement opens, or ``None``.
        window_end: POSIX timestamp the engagement closes, or ``None``.
        intensity: Free-form operator label recorded in the artifact.
        rules_of_engagement: Human-readable constraints, recorded verbatim.
    rY   Úownerztuple[str, ...]r�   r   r8   Ú	max_levelr   rh   Úallowed_tool_classesNúfloat | NoneÚwindow_startÚ
window_endÚmeasuredÚ	intensityr#   Úrules_of_engagementc                óF  — t        | j                  «      j                  «       }|st        d«      ‚t        j                  | d|«       t        d„ | j                  D «       «      }|rt        d„ |D «       «      rt        d«      ‚|D ]  }t        |«       Œ t        j                  | d|«       t        | j                  t        «      st        | j                  t        «      st        d| j                  ›�«      ‚d| j                  cxk  r	t        k  s!n t        d	t        › d
| j                  › �«      ‚t        d„ | j                   D «       «      }|t#        t$        «      z
  }|r(t        dt'        |«      › dt)        t$        «      › d�«      ‚|st        d«      ‚t        j                  | d|«       dD ]F  }t+        | |«      }|€Œt        |t        «      st        |t        t,        f«      rŒ9t        d|› d�«      ‚ | j.                  �0| j0                  �$| j.                  | j0                  k\  rt        d«      ‚t        j                  | dt        | j2                  «      j                  «       xs d«       t        j                  | dt        d„ | j4                  D «       «      «       y )Nz"engagement owner must not be emptyrœ   c              3  óN   K  — | ]  }t        |«      j                  «       –— Œ y ­wrn   )rY   r2   ©rS   rG   s     r%   rU   z8EngagementAuthorization.__post_init__.<locals>.<genexpr>Œ  s   è ø€ ÒG°œ˜F›×)Ñ)×+ÑGùs   ‚#%c              3  ó"   K  — | ]  }| –— Œ	 y ­wrn   r#   r§   s     r%   rU   z8EngagementAuthorization.__post_init__.<locals>.<genexpr>�  s   è ø€ Ò?¨V &œjÑ?ùs   ‚z2engagement must name at least one non-empty targetr�   z-engagement max_level must be an integer, got r   z+engagement max_level must be between 0 and z, got c              3  ój   K  — | ]+  }t        |«      j                  «       j                  «       –— Œ- y ­wrn   ro   )rS   Únames     r%   rU   z8EngagementAuthorization.__post_init__.<locals>.<genexpr>�  s$   è ø€ Ò\¸$œC ›IŸO™OÓ-×3Ñ3×5Ñ\ùrp   z#unknown engagement tool class(es): z	 (known: rV   z-engagement must allow at least one tool classrž   )r    r¡   zengagement z" must be a POSIX timestamp or Nonez1engagement window_start must be before window_endr£   r¢   r¤   c              3  ó2   K  — | ]  }t        |«      –— Œ y ­wrn   )rY   )rS   Úrules     r%   rU   z8EngagementAuthorization.__post_init__.<locals>.<genexpr>·  s   è ø€ ÒA ”#�d—)ÑAùs   ‚)rY   rœ   r2   r   rt   ru   rv   r�   r[   rb   rX   r�   r„   r8   r
   rw   rž   Úsetr   r]   ÚlistÚgetattrÚfloatr    r¡   r£   r¤   )rx   rœ   r�   rG   ÚclassesÚunknownrª   r<   s           r%   r|   z%EngagementAuthorization.__post_init__†  sa  € Ü�D—J‘J“×%Ñ%Ó'ˆÙÜ"Ð#GÓHÐHÜ×Ñ˜4 ¨%Ô0äÑG¸$¿,¹,ÔGÓGˆÙœ#Ñ?°wÔ?Ô?Ü"Ð#WÓXÐXØò 	(ˆFô   Õ'ð	(ô 	×Ñ˜4 ¨GÔ4ä�d—n‘n¤dÔ+´:¸d¿n¹nÌcÔ3RÜ"Ð%RÐSW×SaÑSaÐRdÐ#eÓfÐfØ�D—N‘NÔ:Ô&:Ô:Ü"Ø=Ô>RÐ=Sð TØ—~‘~Ð&ð(óð ô
 Ñ\À$×B[ÑB[Ô\Ó\ˆØœC¤Ó-Ñ-ˆÙÜ"Ø5´f¸W³oÐ5Fð GÜ¤Ó-Ð.¨að1óð ñ Ü"Ð#RÓSÐSÜ×Ñ˜4Ð!7¸ÔAà2ò 	_ˆDÜ˜D $Ó'ˆEØÑ ¤j°¼Ô&=ÄZÐPUÔX[Ô]bÐWcÕEdÜ&¨°T°FÐ:\Ð']Ó^Ð^ð	_ð
 ×ÑÐ)Ø—‘Ð+Ø×!Ñ! T§_¡_Ò4ä"Ð#VÓWÐWä×Ñ˜4 ¬c°$·.±.Ó.A×.GÑ.GÓ.IÒ.WÈZÔXÜ×ÑØØ!ÜÑA¨×(@Ñ(@ÔAÓAõ	
r$   c                ó@   — t         j                  | j                  «      S )z2Return the whitelist derived from :attr:`targets`.)r   r‚   r�   r…   s    r%   ÚscopezEngagementAuthorization.scopeº  s   € ô ×'Ñ'¨¯©Ó5Ð5r$   c           
     óî   — | j                   t        | j                  «      | j                  t	        | j
                  «      | j                  | j                  | j                  t        | j                  «      dœS )zDReturn the artifact as a JSON-compatible mapping, order-independent.)rœ   r�   r�   rž   r    r¡   r£   r¤   )
rœ   r®   r�   r�   r]   rž   r    r¡   r£   r¤   r…   s    r%   Ú	canonicalz!EngagementAuthorization.canonical¿  s[   € ð —Z‘ZÜ˜DŸL™LÓ)ØŸ™Ü$*¨4×+DÑ+DÓ$EØ ×-Ñ-ØŸ/™/ØŸ™Ü#'¨×(@Ñ(@Ó#Añ	
ð 		
r$   c                ó¼   — t        j                  | j                  «       ddt        ¬«      }t	        j
                  |j                  d«      «      j                  «       S )us  Return a stable SHA-256 fingerprint of the whole artifact.

        Stable means: two artifacts with the same terms produce the same digest,
        and any change to any term â€” including the rules of engagement text â€”
        produces a different one. It is a fingerprint, not a signature: it
        proves the terms did not change, not who agreed to them.
        T)ú,r1   )Ú	sort_keysÚ
separatorsÚdefaultzutf-8)ÚjsonÚdumpsr¶   rY   ÚhashlibÚsha256ÚencodeÚ	hexdigest)rx   Úmaterials     r%   ÚdigestzEngagementAuthorization.digestÌ  sE   € ô —:‘:Ø�N‰NÓ¨¸ÌSô
ˆô �~‰~˜hŸo™o¨gÓ6Ó7×AÑAÓCÐCr$   c                ó¤   — |€t        j                   «       n|}| j                  �|| j                  k  ry| j                  �|| j                  kD  ryy)z4Report whether the engagement's time window is open.FT)Útimer    r¡   )rx   ÚnowÚmoments      r%   Ú	is_activez!EngagementAuthorization.is_activeÚ  sJ   € à # ”—‘”°ˆØ×ÑÐ(¨V°d×6GÑ6GÒ-GØØ�?‰?Ð&¨6°D·O±OÒ+CØØr$   c                óx   — t        |t        «      st        |t        «      syd|cxk  xr | j                  k  S c S )zAReport whether ``level`` is at or below the engagement's ceiling.Fr   )rX   r„   r8   r�   )rx   Úlevels     r%   Úallows_levelz$EngagementAuthorization.allows_levelã  s2   € ä�eœTÔ"¬*°U¼CÔ*@ØØ�EÖ+˜TŸ^™^Ñ+Ð+Ñ+Ð+r$   c                óV   — |j                  «       j                  «       | j                  v S )z3Report whether the engagement permits a tool class.)r2   r9   rž   )rx   Ú
tool_classs     r%   Úallows_tool_classz)EngagementAuthorization.allows_tool_classé  s%   € à×ÑÓ!×'Ñ'Ó)¨T×-FÑ-FÐFÐFr$   c                óÌ   — t        |t        «      st        |t        «      sy| j                  |«      syt        j                  t        |«      «      }|€y| j                  |«      S )aZ  Report whether one tool invocation at ``level`` is permitted.

        Both halves have to hold: the level is at or below ``max_level`` *and*
        the tool class that level belongs to is in ``allowed_tool_classes``.
        Checking only the number would let an engagement that set
        ``max_level=3`` silently inherit exploitation even when it named only
        recon.

        ``level`` is narrowed rather than coerced. ``int("2")``, ``int(2.0)``
        and ``int(True)`` all produce an int, so a blind conversion would turn
        a malformed declaration into an authorised one and would let anything
        with ``__int__`` decide its own level. A value that is not already a
        plain int - a bool included, because ``isinstance(True, int)`` is True -
        is refused here, which is the same rule :meth:`allows_level` applies.
        F)rX   r„   r8   rË   r   ÚgetrÎ   )rx   rÊ   rÍ   s      r%   Úengages_levelz%EngagementAuthorization.engages_levelí  sY   € ô  �eœTÔ"¬*°U¼CÔ*@ØØ× Ñ  Ô'ØÜ(×,Ñ,¬S°«ZÓ8ˆ
ØÐØØ×%Ñ% jÓ1Ð1r$   r•   )r–   r   )r–   zdict[str, Any])r–   rY   rn   )rÆ   rŸ   r–   r„   )rÊ   rt   r–   r„   )rÍ   rY   r–   r„   )r   r    r!   r"   r™   r�   rw   rž   r    r¡   r£   r¤   r|   Úpropertyr´   r¶   rÃ   rÈ   rË   rÎ   rÑ   r#   r$   r%   r   r   g  s£   … ñð( ƒJØÓØ€IˆsÓÙ+4°g°YÓ+?Ð˜.Ó?Ø!%€L�,Ó%Ø#€J�Ó#Ø€IˆsÓØ+-Ð˜Ó-ó2
ðh ò6ó ð6ó
ð òDó ðDôó,óGô2r$   r   r˜   )r;   rY   r–   rY   )rG   rY   r–   z
int | None)rG   rt   r–   rY   )r+   rY   r–   rY   )&r"   Ú
__future__r   r¾   r'   r¼   ÚrerÅ   Úcollections.abcr   Údataclassesr   Útypingr   r   Úurllib.parser   Ú&cyberbrain26.security_plane.permissionr	   Ú__all__r
   r™   r   r   rw   r   Úcompiler   r   r   r   r,   rA   rM   rb   r^   r   r   r#   r$   r%   ú<module>rÜ      s*  ðò#õJ #ã Û Û Û 	Û Ý $Ý !ß Ý !å @ò€ð $%Ð �jÓ $ð
 (]€Ð$Ó \ð ØØØñ	.Ð Ð*ó ñ /8¸ÀÐ8IÓ.JÐ Ð+Ó JØ'1 r§z¡zÐ2\Ó']€Ð$Ó ]Ø!$Ð �JÓ $Ø'1 r§z¡zÐ2HÓ'I€Ð$Ó Iô�}ô óóó4"óJ*AóZñ" �$Ô÷ð ó ðñD �$Ô÷\2ð \2ó ñ\2r$   