mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-11-13 01:48:17 +00:00
feat(access-control-prover): 8.1 capture call receiver and arity evidence in the semantic IR
This commit is contained in:
@@ -206,17 +206,52 @@ def _call(node: Any, kinds: dict[str, frozenset[str]]) -> CallSite:
|
||||
# PHP wraps positional and named argument values in an `argument` node.
|
||||
arg_nodes = [arg.named_children[-1] if arg.type == "argument" and arg.named_children else arg for arg in arg_nodes]
|
||||
extra = len(arg_nodes) >= 2 and arg_nodes[1].type in _SEQUENCE
|
||||
callee = _callee_name(node, args_node)
|
||||
return CallSite(
|
||||
order=int(node.end_byte),
|
||||
name=_callee_name(node, args_node),
|
||||
name=callee,
|
||||
line=_line(node),
|
||||
arg_texts=tuple(_text(arg) for arg in arg_nodes),
|
||||
arg_is_constant=tuple(_is_constant(arg, kinds) for arg in arg_nodes),
|
||||
arg_names=tuple(_identifiers(arg) for arg in arg_nodes),
|
||||
extra_arg_is_sequence=extra,
|
||||
receiver=_receiver_text(node, args_node, callee),
|
||||
arity=len(arg_nodes),
|
||||
)
|
||||
|
||||
|
||||
_RECEIVER_SEPARATORS = ("->", "::", "?.", ".")
|
||||
|
||||
|
||||
def _receiver_text(node: Any, args_node: Any, callee: str) -> str | None:
|
||||
parts = [child for child in getattr(node, "named_children", ()) or () if child is not args_node]
|
||||
final_identifier = callee
|
||||
for separator in _RECEIVER_SEPARATORS:
|
||||
final_identifier = final_identifier.rsplit(separator, 1)[-1]
|
||||
final_identifier = final_identifier.strip()
|
||||
for child in parts:
|
||||
receiver = _receiver_before_separator(_text(child), final_identifier)
|
||||
if receiver is not None:
|
||||
return receiver
|
||||
for index, child in enumerate(parts):
|
||||
if _text(child).strip() == final_identifier:
|
||||
before = [text for text in (_text(part).strip() for part in parts[:index]) if text]
|
||||
if before:
|
||||
return ".".join(before)
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def _receiver_before_separator(text: str, final_identifier: str) -> str | None:
|
||||
for separator in _RECEIVER_SEPARATORS:
|
||||
position = text.rfind(separator)
|
||||
if position <= 0:
|
||||
continue
|
||||
if text[position + len(separator) :].strip() == final_identifier:
|
||||
return text[:position].strip() or None
|
||||
return None
|
||||
|
||||
|
||||
def _callee_name(node: Any, args_node: Any) -> str:
|
||||
if node.type in _KIND["php"]["call"]:
|
||||
callee = node.child_by_field_name("name") or node.child_by_field_name("function")
|
||||
|
||||
@@ -27,6 +27,8 @@ class CallSite:
|
||||
arg_names: tuple[tuple[str, ...], ...]
|
||||
extra_arg_is_sequence: bool
|
||||
order: int = 0
|
||||
receiver: str | None = None
|
||||
arity: int | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -202,9 +204,17 @@ def _call_site(node: ast.Call) -> CallSite:
|
||||
arg_is_constant=tuple(arg_const),
|
||||
arg_names=tuple(arg_names),
|
||||
extra_arg_is_sequence=extra_sequence,
|
||||
receiver=_receiver(node.func),
|
||||
arity=len(node.args) + len(node.keywords),
|
||||
)
|
||||
|
||||
|
||||
def _receiver(func: ast.expr) -> str | None:
|
||||
if isinstance(func, ast.Attribute):
|
||||
return _unparse(func.value)
|
||||
return None
|
||||
|
||||
|
||||
def _end_position(node: ast.AST) -> int:
|
||||
return int(getattr(node, "end_lineno", 0) or 0) * 1_000_000 + int(getattr(node, "end_col_offset", 0) or 0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user