Skip to content
Merged
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
16 changes: 7 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,22 @@

module github.com/oracle/macaron

go 1.24.0

toolchain go1.24.13
go 1.25.0

require (
cuelang.org/go v0.15.4
mvdan.cc/sh/v3 v3.12.0
cuelang.org/go v0.16.1
mvdan.cc/sh/v3 v3.13.1
)

require (
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/emicklei/proto v1.14.2 // indirect
github.com/emicklei/proto v1.14.3 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/protocolbuffers/txtpbfmt v0.0.0-20251016062345-16587c79cd91 // indirect
github.com/protocolbuffers/txtpbfmt v0.0.0-20260217160748-a481f6a22f94 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.46.0 // indirect
golang.org/x/text v0.30.0 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/text v0.35.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)
44 changes: 22 additions & 22 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
cuelabs.dev/go/oci/ociregistry v0.0.0-20250722084951-074d06050084 h1:4k1yAtPvZJZQTu8DRY8muBo0LHv6TqtrE0AO5n6IPYs=
cuelabs.dev/go/oci/ociregistry v0.0.0-20250722084951-074d06050084/go.mod h1:4WWeZNxUO1vRoZWAHIG0KZOd6dA25ypyWuwD3ti0Tdc=
cuelang.org/go v0.15.4 h1:lrkTDhqy8dveHgX1ZLQ6WmgbhD8+rXa0fD25hxEKYhw=
cuelang.org/go v0.15.4/go.mod h1:NYw6n4akZcTjA7QQwJ1/gqWrrhsN4aZwhcAL0jv9rZE=
cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819 h1:Zh+Ur3OsoWpvALHPLT45nOekHkgOt+IOfutBbPqM17I=
cuelabs.dev/go/oci/ociregistry v0.0.0-20251212221603-3adeb8663819/go.mod h1:WjmQxb+W6nVNCgj8nXrF24lIz95AHwnSl36tpjDZSU8=
cuelang.org/go v0.16.1 h1:iPN1lHZd2J0hjcr8hfq9PnIGk7VfPkKFfxH4de+m9sE=
cuelang.org/go v0.16.1/go.mod h1:/aW3967FeWC5Hc1cDrN4Z4ICVApdMi83wO5L3uF/1hM=
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/emicklei/proto v1.14.2 h1:wJPxPy2Xifja9cEMrcA/g08art5+7CGJNFNk35iXC1I=
github.com/emicklei/proto v1.14.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/emicklei/proto v1.14.3 h1:zEhlzNkpP8kN6utonKMzlPfIvy82t5Kb9mufaJxSe1Q=
github.com/emicklei/proto v1.14.3/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
Expand All @@ -28,28 +28,28 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/protocolbuffers/txtpbfmt v0.0.0-20251016062345-16587c79cd91 h1:s1LvMaU6mVwoFtbxv/rCZKE7/fwDmDY684FfUe4c1Io=
github.com/protocolbuffers/txtpbfmt v0.0.0-20251016062345-16587c79cd91/go.mod h1:JSbkp0BviKovYYt9XunS95M3mLPibE9bGg+Y95DsEEY=
github.com/protocolbuffers/txtpbfmt v0.0.0-20260217160748-a481f6a22f94 h1:2PC6Ql3jipz1KvBlqUHjjk6v4aMwE86mfDu1XMH0LR8=
github.com/protocolbuffers/txtpbfmt v0.0.0-20260217160748-a481f6a22f94/go.mod h1:JSbkp0BviKovYYt9XunS95M3mLPibE9bGg+Y95DsEEY=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
mvdan.cc/sh/v3 v3.12.0 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI=
mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg=
mvdan.cc/sh/v3 v3.13.1 h1:DP3TfgZhDkT7lerUdnp6PTGKyxxzz6T+cOlY/xEvfWk=
mvdan.cc/sh/v3 v3.13.1/go.mod h1:lXJ8SexMvEVcHCoDvAGLZgFJ9Wsm2sulmoNEXGhYZD0=
134 changes: 133 additions & 1 deletion src/macaron/code_analyzer/dataflow_analysis/bash.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,27 @@ def with_stdout(
self.gha_expr_map_items,
)

def with_stdout_stderr(
self, stdout_scope: core.ContextRef[facts.Scope], stdout_loc: facts.LocationSpecifier
) -> BashScriptContext:
"""Return a modified bash script context with the given stdout and stderr.

TODO currently stderr is not defined in BashScriptContext, so we cannot support here neither.
Add stderr to BashScriptContext to be able to support it here as well.
"""
return BashScriptContext(
self.outer_context,
self.filesystem,
self.env,
self.func_decls,
self.stdin_scope,
self.stdin_loc,
stdout_scope,
stdout_loc,
self.source_filepath,
self.gha_expr_map_items,
)

def with_gha_expr_map(self, gha_expr_map: dict[str, str]) -> BashScriptContext:
"""Return a modified bash script context with GitHub-expression placeholder mappings.

Expand Down Expand Up @@ -772,7 +793,11 @@ def build_pipe() -> core.Node:

return {"default": build_pipe}
case bashparser_model.BinCmdOperators.PipeAll.value:
pass

def build_pipeall() -> core.Node:
return BashPipeAllNode.create(cmd, self.context.get_non_owned())

return {"default": build_pipeall}
case bashparser_model.BinCmdOperators.AndStmt.value:

def build_and() -> core.Node:
Expand Down Expand Up @@ -1272,6 +1297,113 @@ def create(
return BashPipeNode(definition=pipe_cmd, lhs=lhs, rhs=rhs, context=pipe_context)


class BashPipeAllNode(core.ControlFlowGraphNode):
"""Control flow node representing a Bash pipe (``|&``) binary command.

Control flow structure consists of executing the left-hand side, followed by the right-hand side.
A pipe scope and location is introduced to model the piping of the
output from the first command to the input of the second command. Compared to
a normal pipe (``|``), ``|&`` pipes both stdout and stderr (file descriptors 1 and 2)
of the left command into stdin of the right.
"""

#: Parsed pipe all binary command AST.
definition: bashparser_model.BinaryCmd
#: Left-hand side (first) command.
lhs: BashStatementNode
#: Right-hand side (second) command.
rhs: BashStatementNode
#: Pipe context.
context: core.ContextRef[BashPipeContext]
#: Control flow graph.
_cfg: core.ControlFlowGraph

def __init__(
self,
definition: bashparser_model.BinaryCmd,
lhs: BashStatementNode,
rhs: BashStatementNode,
context: core.ContextRef[BashPipeContext],
) -> None:
"""Initialize Bash pipe all node.

Typically, construction should be done via the create function rather than using this constructor directly.

Parameters
----------
definition: bashparser_model.BinaryCmd
Parsed pipe all binary command AST.
lhs: BashStatementNode
Left-hand side (first) command.
rhs: BashStatementNode
Right-hand side (second) command.
context: core.ContextRef[BashPipeContext]
Pipe context.
"""
super().__init__()
self.definition = definition
self.lhs = lhs
self.rhs = rhs
self.context = context

self._cfg = core.ControlFlowGraph(self.lhs)
self._cfg.add_successor(self.lhs, core.DEFAULT_EXIT, self.rhs)
self._cfg.add_successor(self.rhs, core.DEFAULT_EXIT, core.DEFAULT_EXIT)

def children(self) -> Iterator[core.Node]:
"""Yield the subcommands."""
yield self.lhs
yield self.rhs

def get_entry(self) -> core.Node:
"""Return the entry node (the lhs node)."""
return self._cfg.get_entry()

def get_successors(self, node: core.Node, exit_type: core.ExitType) -> set[core.Node | core.ExitType]:
"""Return the successor for a given node.

Returns a propagated early exit of the same type in the case of a BashExit or BashReturn exit type.
"""
if isinstance(exit_type, (BashExit, BashReturn)):
return {exit_type}
return self._cfg.get_successors(node, core.DEFAULT_EXIT)

def get_exit_state_transfer_filter(self) -> core.StateTransferFilter:
"""Return state transfer filter to clear scopes owned by this node after this node exits."""
return core.ExcludedScopesStateTransferFilter(core.get_owned_scopes(self.context))

def get_printable_properties_table(self) -> dict[str, set[tuple[str | None, str]]]:
"""Return a properties table containing the line number and scopes."""
result: dict[str, set[tuple[str | None, str]]] = {}
result["line num (in script)"] = {(None, str(self.definition["Pos"]["Line"]))}
printing.add_context_owned_scopes_to_properties_table(result, self.context)
return result

@staticmethod
def create(
pipe_cmd: bashparser_model.BinaryCmd, context: core.NonOwningContextRef[BashScriptContext]
) -> BashPipeAllNode:
"""Create Bash pipe all node from pipe binary command AST.

Parameters
----------
pipe_cmd: bashparser_model.BinaryCmd
Parsed pipe binary command AST.
context: core.NonOwningContextRef[BashScriptContext]
Bash script context.
"""
pipe_context = core.OwningContextRef(BashPipeContext.create(context))
piped_from_context = core.NonOwningContextRef(
context.ref.with_stdout_stderr(pipe_context.ref.pipe_scope.get_non_owned(), pipe_context.ref.pipe_loc)
)
piped_to_context = core.NonOwningContextRef(
context.ref.with_stdin(pipe_context.ref.pipe_scope.get_non_owned(), pipe_context.ref.pipe_loc)
)
lhs = BashStatementNode(pipe_cmd["X"], piped_from_context)
rhs = BashStatementNode(pipe_cmd["Y"], piped_to_context)
return BashPipeAllNode(definition=pipe_cmd, lhs=lhs, rhs=rhs, context=pipe_context)


class BashAndNode(core.ControlFlowGraphNode):
"""Control flow node representing a Bash AND ("&&") binary command.

Expand Down
Loading