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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Source models defined using models-as-data now work for variadic parameters.
10 changes: 10 additions & 0 deletions go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll
Original file line number Diff line number Diff line change
Expand Up @@ -458,3 +458,13 @@ class ContentApprox = Unit;
/** Gets an approximated value for content `c`. */
pragma[inline]
ContentApprox getContentApprox(Content c) { any() }

/**
* Holds if the the content `c` is a container.
*/
predicate containerContent(ContentSet c) {
c instanceof ArrayContent or
c instanceof CollectionContent or
c instanceof MapKeyContent or
c instanceof MapValueContent
}
38 changes: 30 additions & 8 deletions go/ql/lib/semmle/go/dataflow/internal/TaintTrackingUtil.qll
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,21 @@ predicate localExprTaint(Expr src, Expr sink) {
* Holds if taint can flow in one local step from `src` to `sink`.
*/
predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
DataFlow::localFlowStep(src, sink) or
localAdditionalTaintStep(src, sink, _) or
DataFlow::localFlowStep(src, sink)
or
localAdditionalTaintStep(src, sink, _)
or
// Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(src, sink, _)
or
// Treat container flow as taint for the local taint flow relation
exists(DataFlow::Content c | DataFlowPrivate::containerContent(c) |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this defined here and not in localAdditionalTaintStep?

Copy link
Contributor Author

@owen-mc owen-mc Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In increasing order of how good an answer it is:
(a) It's what they do in java.
(b) I copied it before I realised that it wouldn't affect global taint flow at all.
(c) I think it is helpful. Six months ago I tried to convert old-style models for variadic functions to MaD. One of the sticking points was "Some barrier guard definitions use localTaintStep to detect taint flow, but it doesn't work flow through an implicit varargs slice." With this added (for container reads and container writes) we get that localTaint includes flow into and out of arrays, including through implicit varargs slices.

Copy link
Contributor

@smowton smowton Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I think I see: you want both reads and stores to appear according to the handy utility localTaintStep, but only reads to be contributed to the additional-taint-step for global dataflow's purposes?

If so, suggest three things:

  1. I note @aschackmull suggested the summary-getter step should be passed to the dataflow lib (i.e. in isAdditionalTaintStep`)
  2. You might as well omit adding the read steps again here since localAdditionalTaintStep is already included -- instead just comment that the read steps are already taken care of
  3. Add a comment explaining why we want read steps given to the dataflow lib but not stores

Copy link
Contributor Author

@owen-mc owen-mc Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I don't think we shoudl do this because the QLDoc for summaryGetterStep says
   * NOTE: This step should not be used in global data-flow/taint-tracking, but may
   * be useful to include in the exposed local data-flow/taint-tracking relations.

Note also that something related is included in readStep (and storeStep).
2. Done.
3. I'm not exactly sure what to say. I've put "Treat container read steps as taint for global taint flow." @aschackmull can you suggest a more detailed explanation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the qldoc that Owen referenced says, the summary-getter step should not be passed to global data flow in any form (neither as a read step nor as a taint step). To elaborate: Container read steps should be passed to data flow as taint steps. But read steps and summary-getter steps are separate things: A read step is atomic, whereas a summary-getter step represents a flow path through a callable with a nested read step, such that the entire thing can be summarized and treated as a read step in those contexts that aren't capable of doing such summarization. And data flow notably is perfectly capable of such summarization, so it only needs to know about the atomic read steps.

DataFlowPrivate::readStep(src, c, sink) or
DataFlowPrivate::storeStep(src, c, sink) or
FlowSummaryImpl::Private::Steps::summaryGetterStep(src, c, sink, _) or
FlowSummaryImpl::Private::Steps::summarySetterStep(src, c, sink, _)
Comment on lines +40 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't include store steps, just the reads.

Suggested change
DataFlowPrivate::readStep(src, c, sink) or
DataFlowPrivate::storeStep(src, c, sink) or
FlowSummaryImpl::Private::Steps::summaryGetterStep(src, c, sink, _) or
FlowSummaryImpl::Private::Steps::summarySetterStep(src, c, sink, _)
DataFlowPrivate::readStep(src, c, sink) or
FlowSummaryImpl::Private::Steps::summaryGetterStep(src, c, sink, _)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait, I guess this isn't the relation that gets passed to global data flow? Then it's fine.

)
}

private Type getElementType(Type containerType) {
Expand Down Expand Up @@ -88,12 +98,18 @@ class AdditionalTaintStep extends Unit {
*/
predicate localAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ, string model) {
(
referenceStep(pred, succ) or
elementWriteStep(pred, succ) or
fieldReadStep(pred, succ) or
elementStep(pred, succ) or
tupleStep(pred, succ) or
stringConcatStep(pred, succ) or
referenceStep(pred, succ)
or
elementWriteStep(pred, succ)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this step doing here? I realise it isn't added in this PR, but it looks wrong: store steps shouldn't be taint steps.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The QLDoc says "Holds if there is an assignment of the form succ[idx] = pred, meaning that pred may taint succ." So when you taint an element of an array, you taint the whole array. (And the same for slices, which are just like arrays.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see that you could take a different approach, where only the array element is tainted, and then if the array is used without an array access then hopefully there is an implicit read step that will mean that the whole array is tainted. I could try removing this line and see what effect it has. But I think that shouldn't be part of this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could try removing this line and see what effect it has

I think that should be attempted, yes.

But I think that shouldn't be part of this PR.

Agree.

or
fieldReadStep(pred, succ)
or
elementStep(pred, succ)
or
tupleStep(pred, succ)
or
stringConcatStep(pred, succ)
or
sliceStep(pred, succ)
) and
model = ""
Expand Down Expand Up @@ -163,6 +179,12 @@ predicate elementStep(DataFlow::Node pred, DataFlow::Node succ) {
// only step into the value, not the index
succ.asInstruction() = IR::extractTupleElement(nextEntry, 1)
)
or
exists(DataFlow::ImplicitVarargsSlice ivs |
pred.(DataFlow::PostUpdateNode).getPreUpdateNode() = ivs and
succ.(DataFlow::PostUpdateNode).getPreUpdateNode() =
ivs.getCallNode().getAnImplicitVarargsArgument()
)
}

/** Holds if taint flows from `pred` to `succ` via an extract tuple operation. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,36 @@
| main.go:38:19:38:19 | 3 | main.go:38:7:38:20 | slice literal |
| main.go:39:8:39:25 | []type{args} | main.go:39:8:39:25 | call to append |
| main.go:39:15:39:15 | s | main.go:39:8:39:25 | call to append |
| main.go:39:18:39:18 | 4 | main.go:39:8:39:25 | []type{args} |
| main.go:39:21:39:21 | 5 | main.go:39:8:39:25 | []type{args} |
| main.go:39:24:39:24 | 6 | main.go:39:8:39:25 | []type{args} |
| main.go:40:15:40:15 | s | main.go:40:8:40:23 | call to append |
| main.go:40:18:40:19 | s1 | main.go:40:8:40:23 | call to append |
| main.go:42:10:42:11 | s4 | main.go:38:2:38:2 | definition of s |
| main.go:47:20:47:21 | next key-value pair in range | main.go:47:2:50:2 | range statement[0] |
| main.go:47:20:47:21 | next key-value pair in range | main.go:47:2:50:2 | range statement[1] |
| main.go:47:20:47:21 | xs | main.go:47:2:50:2 | range statement[1] |
| main.go:56:8:56:11 | true | main.go:56:2:56:3 | ch |
| main.go:57:4:57:5 | ch | main.go:57:2:57:5 | <-... |
| strings.go:9:24:9:24 | s | strings.go:9:8:9:38 | call to Replace |
| strings.go:9:32:9:34 | "_" | strings.go:9:8:9:38 | call to Replace |
| strings.go:10:27:10:27 | s | strings.go:10:8:10:42 | call to ReplaceAll |
| strings.go:10:35:10:41 | "&amp;" | strings.go:10:8:10:42 | call to ReplaceAll |
| strings.go:11:9:11:26 | []type{args} | strings.go:11:9:11:26 | call to Sprint |
| strings.go:11:9:11:26 | call to Sprint | strings.go:11:9:11:50 | ...+... |
| strings.go:11:9:11:50 | ...+... | strings.go:11:9:11:69 | ...+... |
| strings.go:11:20:11:21 | s2 | strings.go:11:9:11:26 | []type{args} |
| strings.go:11:20:11:21 | s2 | strings.go:11:9:11:26 | call to Sprint |
| strings.go:11:24:11:25 | s3 | strings.go:11:9:11:26 | []type{args} |
| strings.go:11:24:11:25 | s3 | strings.go:11:9:11:26 | call to Sprint |
| strings.go:11:30:11:50 | []type{args} | strings.go:11:30:11:50 | call to Sprintf |
| strings.go:11:30:11:50 | call to Sprintf | strings.go:11:9:11:50 | ...+... |
| strings.go:11:42:11:45 | "%q" | strings.go:11:30:11:50 | call to Sprintf |
| strings.go:11:48:11:49 | s2 | strings.go:11:30:11:50 | []type{args} |
| strings.go:11:48:11:49 | s2 | strings.go:11:30:11:50 | call to Sprintf |
| strings.go:11:54:11:69 | []type{args} | strings.go:11:54:11:69 | call to Sprintln |
| strings.go:11:54:11:69 | call to Sprintln | strings.go:11:9:11:69 | ...+... |
| strings.go:11:67:11:68 | s3 | strings.go:11:54:11:69 | []type{args} |
| strings.go:11:67:11:68 | s3 | strings.go:11:54:11:69 | call to Sprintln |
| url.go:12:14:12:48 | call to PathUnescape | url.go:12:3:12:48 | ... = ...[0] |
| url.go:12:14:12:48 | call to PathUnescape | url.go:12:3:12:48 | ... = ...[1] |
Expand All @@ -39,17 +51,25 @@
| url.go:27:9:27:30 | call to ParseRequestURI | url.go:27:2:27:30 | ... = ...[1] |
| url.go:27:29:27:29 | s | url.go:27:2:27:30 | ... = ...[0] |
| url.go:28:14:28:14 | u | url.go:28:14:28:28 | call to EscapedPath |
| url.go:28:14:28:28 | call to EscapedPath | url.go:28:2:28:29 | []type{args} |
| url.go:29:14:29:14 | u | url.go:29:14:29:25 | call to Hostname |
| url.go:29:14:29:25 | call to Hostname | url.go:29:2:29:26 | []type{args} |
| url.go:30:11:30:11 | u | url.go:30:2:30:27 | ... := ...[0] |
| url.go:30:11:30:27 | call to MarshalBinary | url.go:30:2:30:27 | ... := ...[0] |
| url.go:30:11:30:27 | call to MarshalBinary | url.go:30:2:30:27 | ... := ...[1] |
| url.go:31:2:31:16 | []type{args} | url.go:30:2:30:3 | definition of bs |
| url.go:31:14:31:15 | bs | url.go:31:2:31:16 | []type{args} |
| url.go:32:9:32:9 | u | url.go:32:2:32:23 | ... = ...[0] |
| url.go:32:9:32:23 | call to Parse | url.go:32:2:32:23 | ... = ...[0] |
| url.go:32:9:32:23 | call to Parse | url.go:32:2:32:23 | ... = ...[1] |
| url.go:32:17:32:22 | "/foo" | url.go:32:2:32:23 | ... = ...[0] |
| url.go:33:14:33:14 | u | url.go:33:14:33:21 | call to Port |
| url.go:33:14:33:21 | call to Port | url.go:33:2:33:22 | []type{args} |
| url.go:34:2:34:23 | []type{args} | url.go:34:14:34:22 | call to Query |
| url.go:34:14:34:14 | u | url.go:34:14:34:22 | call to Query |
| url.go:34:14:34:22 | call to Query | url.go:34:2:34:23 | []type{args} |
| url.go:35:14:35:14 | u | url.go:35:14:35:27 | call to RequestURI |
| url.go:35:14:35:27 | call to RequestURI | url.go:35:2:35:28 | []type{args} |
| url.go:36:6:36:6 | u | url.go:36:6:36:26 | call to ResolveReference |
| url.go:36:25:36:25 | u | url.go:36:6:36:26 | call to ResolveReference |
| url.go:41:17:41:20 | "me" | url.go:41:8:41:21 | call to User |
Expand All @@ -58,27 +78,35 @@
| url.go:43:11:43:12 | ui | url.go:43:2:43:23 | ... := ...[0] |
| url.go:43:11:43:23 | call to Password | url.go:43:2:43:23 | ... := ...[0] |
| url.go:43:11:43:23 | call to Password | url.go:43:2:43:23 | ... := ...[1] |
| url.go:44:14:44:15 | pw | url.go:44:2:44:16 | []type{args} |
| url.go:45:14:45:15 | ui | url.go:45:14:45:26 | call to Username |
| url.go:45:14:45:26 | call to Username | url.go:45:2:45:27 | []type{args} |
| url.go:50:10:50:26 | call to ParseQuery | url.go:50:2:50:26 | ... := ...[0] |
| url.go:50:10:50:26 | call to ParseQuery | url.go:50:2:50:26 | ... := ...[1] |
| url.go:50:25:50:25 | q | url.go:50:2:50:26 | ... := ...[0] |
| url.go:51:14:51:14 | v | url.go:51:14:51:23 | call to Encode |
| url.go:51:14:51:23 | call to Encode | url.go:51:2:51:24 | []type{args} |
| url.go:52:14:52:14 | v | url.go:52:14:52:26 | call to Get |
| url.go:52:14:52:26 | call to Get | url.go:52:2:52:27 | []type{args} |
| url.go:57:16:57:39 | call to JoinPath | url.go:57:2:57:39 | ... := ...[0] |
| url.go:57:16:57:39 | call to JoinPath | url.go:57:2:57:39 | ... := ...[1] |
| url.go:57:29:57:29 | q | url.go:57:2:57:39 | ... := ...[0] |
| url.go:57:32:57:38 | "clean" | url.go:57:2:57:39 | ... := ...[0] |
| url.go:57:32:57:38 | "clean" | url.go:57:16:57:39 | []type{args} |
| url.go:58:16:58:45 | call to JoinPath | url.go:58:2:58:45 | ... := ...[0] |
| url.go:58:16:58:45 | call to JoinPath | url.go:58:2:58:45 | ... := ...[1] |
| url.go:58:29:58:35 | "clean" | url.go:58:2:58:45 | ... := ...[0] |
| url.go:58:38:58:44 | joined1 | url.go:58:2:58:45 | ... := ...[0] |
| url.go:58:38:58:44 | joined1 | url.go:58:16:58:45 | []type{args} |
| url.go:59:14:59:31 | call to Parse | url.go:59:2:59:31 | ... := ...[0] |
| url.go:59:14:59:31 | call to Parse | url.go:59:2:59:31 | ... := ...[1] |
| url.go:59:24:59:30 | joined2 | url.go:59:2:59:31 | ... := ...[0] |
| url.go:60:15:60:19 | asUrl | url.go:60:15:60:37 | call to JoinPath |
| url.go:60:30:60:36 | "clean" | url.go:60:15:60:37 | []type{args} |
| url.go:60:30:60:36 | "clean" | url.go:60:15:60:37 | call to JoinPath |
| url.go:65:17:65:48 | call to Parse | url.go:65:2:65:48 | ... := ...[0] |
| url.go:65:17:65:48 | call to Parse | url.go:65:2:65:48 | ... := ...[1] |
| url.go:65:27:65:47 | "http://harmless.org" | url.go:65:2:65:48 | ... := ...[0] |
| url.go:66:9:66:16 | cleanUrl | url.go:66:9:66:28 | call to JoinPath |
| url.go:66:27:66:27 | q | url.go:66:9:66:28 | []type{args} |
| url.go:66:27:66:27 | q | url.go:66:9:66:28 | call to JoinPath |
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ func main() {

var variadicSource string
test.VariadicSource(&variadicSource)
sink(variadicSource) // $ MISSING: hasTaintFlow="variadicSource"
sink(variadicSource) // $ hasTaintFlow="variadicSource"
sink(&variadicSource) // $ hasTaintFlow="&..."

var variadicSourcePtr *string
test.VariadicSource(variadicSourcePtr)
sink(variadicSourcePtr) // $ hasTaintFlow="variadicSourcePtr"
sink(*variadicSourcePtr) // $ hasTaintFlow="star expression"

test.VariadicSink(source()) // $ hasTaintFlow="[]type{args}"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ invalidModelRow
| io.go:14:31:14:43 | "some string" | io.go:14:13:14:44 | call to NewReader |
| io.go:16:3:16:3 | definition of w | io.go:16:23:16:27 | &... |
| io.go:16:3:16:3 | definition of w | io.go:16:30:16:34 | &... |
| io.go:16:8:16:35 | []type{args} | io.go:16:23:16:27 | &... |
| io.go:16:8:16:35 | []type{args} | io.go:16:30:16:34 | &... |
| io.go:16:23:16:27 | &... | io.go:15:7:15:10 | definition of buf1 |
| io.go:16:23:16:27 | &... | io.go:16:8:16:35 | []type{args} |
| io.go:16:24:16:27 | buf1 | io.go:16:23:16:27 | &... |
| io.go:16:30:16:34 | &... | io.go:15:13:15:16 | definition of buf2 |
| io.go:16:30:16:34 | &... | io.go:16:8:16:35 | []type{args} |
| io.go:16:31:16:34 | buf2 | io.go:16:30:16:34 | &... |
| io.go:18:14:18:19 | reader | io.go:16:3:16:3 | definition of w |
| io.go:22:31:22:43 | "some string" | io.go:22:13:22:44 | call to NewReader |
Expand All @@ -27,8 +31,10 @@ invalidModelRow
| io.go:39:11:39:19 | call to Pipe | io.go:39:3:39:19 | ... := ...[0] |
| io.go:39:11:39:19 | call to Pipe | io.go:39:3:39:19 | ... := ...[1] |
| io.go:40:17:40:31 | "some string\\n" | io.go:39:6:39:6 | definition of w |
| io.go:40:17:40:31 | "some string\\n" | io.go:40:3:40:32 | []type{args} |
| io.go:43:16:43:16 | r | io.go:42:3:42:5 | definition of buf |
| io.go:44:13:44:15 | buf | io.go:44:13:44:24 | call to String |
| io.go:44:13:44:24 | call to String | io.go:44:3:44:25 | []type{args} |
| io.go:48:31:48:43 | "some string" | io.go:48:13:48:44 | call to NewReader |
| io.go:50:18:50:23 | reader | io.go:49:3:49:5 | definition of buf |
| io.go:54:31:54:43 | "some string" | io.go:54:13:54:44 | call to NewReader |
Expand All @@ -46,8 +52,14 @@ invalidModelRow
| io.go:82:27:82:36 | "reader1 " | io.go:82:9:82:37 | call to NewReader |
| io.go:83:27:83:36 | "reader2 " | io.go:83:9:83:37 | call to NewReader |
| io.go:84:27:84:35 | "reader3" | io.go:84:9:84:36 | call to NewReader |
| io.go:85:8:85:33 | []type{args} | io.go:82:3:82:4 | definition of r1 |
| io.go:85:8:85:33 | []type{args} | io.go:83:3:83:4 | definition of r2 |
| io.go:85:8:85:33 | []type{args} | io.go:84:3:84:4 | definition of r3 |
| io.go:85:23:85:24 | r1 | io.go:85:8:85:33 | []type{args} |
| io.go:85:23:85:24 | r1 | io.go:85:8:85:33 | call to MultiReader |
| io.go:85:27:85:28 | r2 | io.go:85:8:85:33 | []type{args} |
| io.go:85:27:85:28 | r2 | io.go:85:8:85:33 | call to MultiReader |
| io.go:85:31:85:32 | r3 | io.go:85:8:85:33 | []type{args} |
| io.go:85:31:85:32 | r3 | io.go:85:8:85:33 | call to MultiReader |
| io.go:86:22:86:22 | r | io.go:86:11:86:19 | selection of Stdout |
| io.go:89:26:89:38 | "some string" | io.go:89:8:89:39 | call to NewReader |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,13 @@ edges
| passwords.go:34:28:34:35 | password | passwords.go:34:14:34:35 | ...+... | provenance | Config |
| passwords.go:36:2:36:5 | definition of obj1 | passwords.go:39:14:39:17 | obj1 | provenance | |
| passwords.go:36:2:36:5 | definition of obj1 | passwords.go:39:14:39:17 | obj1 | provenance | |
| passwords.go:36:10:38:2 | struct literal | passwords.go:39:14:39:17 | obj1 | provenance | |
| passwords.go:36:10:38:2 | struct literal | passwords.go:39:14:39:17 | obj1 | provenance | |
| passwords.go:36:10:38:2 | struct literal | passwords.go:36:2:36:5 | definition of obj1 | provenance | |
| passwords.go:37:13:37:13 | x | passwords.go:36:10:38:2 | struct literal | provenance | Config |
| passwords.go:39:2:39:18 | []type{args} [array] | passwords.go:36:2:36:5 | definition of obj1 | provenance | |
| passwords.go:39:14:39:17 | obj1 | passwords.go:39:2:39:18 | []type{args} [array] | provenance | |
| passwords.go:41:2:41:5 | definition of obj2 | passwords.go:44:14:44:17 | obj2 | provenance | |
| passwords.go:41:2:41:5 | definition of obj2 | passwords.go:44:14:44:17 | obj2 | provenance | |
| passwords.go:41:10:43:2 | struct literal | passwords.go:44:14:44:17 | obj2 | provenance | |
| passwords.go:41:10:43:2 | struct literal | passwords.go:44:14:44:17 | obj2 | provenance | |
| passwords.go:41:10:43:2 | struct literal | passwords.go:41:2:41:5 | definition of obj2 | provenance | |
| passwords.go:42:6:42:13 | password | passwords.go:41:10:43:2 | struct literal | provenance | Config |
| passwords.go:44:2:44:18 | []type{args} [array] | passwords.go:41:2:41:5 | definition of obj2 | provenance | |
| passwords.go:44:14:44:17 | obj2 | passwords.go:44:2:44:18 | []type{args} [array] | provenance | |
Expand All @@ -85,8 +83,7 @@ edges
| passwords.go:48:11:48:18 | password | passwords.go:46:6:46:9 | definition of obj3 | provenance | Config |
| passwords.go:85:2:85:14 | definition of utilityObject | passwords.go:88:14:88:26 | utilityObject | provenance | |
| passwords.go:85:2:85:14 | definition of utilityObject | passwords.go:88:14:88:26 | utilityObject | provenance | |
| passwords.go:85:19:87:2 | struct literal | passwords.go:88:14:88:26 | utilityObject | provenance | |
| passwords.go:85:19:87:2 | struct literal | passwords.go:88:14:88:26 | utilityObject | provenance | |
| passwords.go:85:19:87:2 | struct literal | passwords.go:85:2:85:14 | definition of utilityObject | provenance | |
| passwords.go:86:16:86:36 | call to make | passwords.go:85:19:87:2 | struct literal | provenance | Config |
| passwords.go:88:2:88:27 | []type{args} [array] | passwords.go:85:2:85:14 | definition of utilityObject | provenance | |
| passwords.go:88:14:88:26 | utilityObject | passwords.go:88:2:88:27 | []type{args} [array] | provenance | |
Expand All @@ -102,12 +99,9 @@ edges
| passwords.go:118:2:118:7 | definition of config [x] | passwords.go:126:14:126:19 | config [x] | provenance | |
| passwords.go:118:2:118:7 | definition of config [y] | passwords.go:125:14:125:19 | config [y] | provenance | |
| passwords.go:118:2:118:7 | definition of config [y] | passwords.go:127:14:127:19 | config [y] | provenance | |
| passwords.go:118:12:123:2 | struct literal | passwords.go:125:14:125:19 | config | provenance | |
| passwords.go:118:12:123:2 | struct literal | passwords.go:125:14:125:19 | config | provenance | |
| passwords.go:118:12:123:2 | struct literal [x] | passwords.go:125:14:125:19 | config [x] | provenance | |
| passwords.go:118:12:123:2 | struct literal [x] | passwords.go:126:14:126:19 | config [x] | provenance | |
| passwords.go:118:12:123:2 | struct literal [y] | passwords.go:125:14:125:19 | config [y] | provenance | |
| passwords.go:118:12:123:2 | struct literal [y] | passwords.go:127:14:127:19 | config [y] | provenance | |
| passwords.go:118:12:123:2 | struct literal | passwords.go:118:2:118:7 | definition of config | provenance | |
| passwords.go:118:12:123:2 | struct literal [x] | passwords.go:118:2:118:7 | definition of config [x] | provenance | |
| passwords.go:118:12:123:2 | struct literal [y] | passwords.go:118:2:118:7 | definition of config [y] | provenance | |
| passwords.go:119:13:119:13 | x | passwords.go:118:12:123:2 | struct literal | provenance | Config |
| passwords.go:121:13:121:20 | password | passwords.go:118:12:123:2 | struct literal | provenance | Config |
| passwords.go:121:13:121:20 | password | passwords.go:118:12:123:2 | struct literal [x] | provenance | |
Expand Down
Loading