@eagleoutice/flowr 2.10.3 → 2.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,7 +24,7 @@ It offers a wide variety of features, for example:
24
24
 
25
25
  ```shell
26
26
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
27
- flowR repl using flowR v2.10.2, R grammar v14 (tree-sitter engine)
27
+ flowR repl using flowR v2.10.3, R grammar v14 (tree-sitter engine)
28
28
  R> :query @linter "read.csv(\"/root/x.txt\")"
29
29
  ```
30
30
 
@@ -35,17 +35,17 @@ It offers a wide variety of features, for example:
35
35
  ```text
36
36
  Query: linter (2 ms)
37
37
  ╰ Deprecated Functions (deprecated-functions):
38
- ╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0
38
+ ╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0
39
39
  ╰ File Path Validity (file-path-validity):
40
40
  ╰ certain:
41
41
  ╰ Path `/root/x.txt` at 1.1-23
42
- ╰ Metadata: totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 1, processTimeMs: 0
42
+ ╰ Metadata: totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 0
43
43
  ╰ Seeded Randomness (seeded-randomness):
44
44
  ╰ Metadata: consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 0, processTimeMs: 0
45
45
  ╰ Absolute Paths (absolute-file-paths):
46
46
  ╰ certain:
47
47
  ╰ Path `/root/x.txt` at 1.1-23
48
- ╰ Metadata: totalConsidered: 1, totalUnknown: 0, searchTimeMs: 0, processTimeMs: 0
48
+ ╰ Metadata: totalConsidered: 1, totalUnknown: 0, searchTimeMs: 1, processTimeMs: 0
49
49
  ╰ Unused Definitions (unused-definitions):
50
50
  ╰ Metadata: totalConsidered: 0, searchTimeMs: 0, processTimeMs: 0
51
51
  ╰ Naming Convention (naming-convention):
@@ -53,12 +53,12 @@ It offers a wide variety of features, for example:
53
53
  ╰ Network Functions (network-functions):
54
54
  ╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0
55
55
  ╰ Dataframe Access Validation (dataframe-access-validation):
56
- ╰ Metadata: numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 1
56
+ ╰ Metadata: numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 0
57
57
  ╰ Dead Code (dead-code):
58
58
  ╰ Metadata: consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0
59
59
  ╰ Useless Loops (useless-loop):
60
60
  ╰ Metadata: numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0
61
- ╰ Problematic eval (problematic-eval):
61
+ ╰ Problematic inputs (problematic-inputs):
62
62
  ╰ Metadata: searchTimeMs: 0, processTimeMs: 0
63
63
  ╰ Stop without call.=False argument (stop-call):
64
64
  ╰ Metadata: consideredNodes: 0, searchTimeMs: 0, processTimeMs: 0
@@ -86,15 +86,15 @@ It offers a wide variety of features, for example:
86
86
 
87
87
  _Results (prettified and summarized):_
88
88
 
89
- Query: **linter** (3 ms)\
89
+ Query: **linter** (2 ms)\
90
90
     ╰ **Deprecated Functions** (deprecated-functions):\
91
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0</code>\
91
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0</code>\
92
92
  &nbsp;&nbsp;&nbsp;╰ **File Path Validity** (file-path-validity):\
93
93
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ certain:\
94
94
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
95
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 0</code>\
95
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 1, processTimeMs: 0</code>\
96
96
  &nbsp;&nbsp;&nbsp;╰ **Seeded Randomness** (seeded-randomness):\
97
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 1, processTimeMs: 0</code>\
97
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 0, processTimeMs: 0</code>\
98
98
  &nbsp;&nbsp;&nbsp;╰ **Absolute Paths** (absolute-file-paths):\
99
99
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ certain:\
100
100
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
@@ -104,24 +104,24 @@ It offers a wide variety of features, for example:
104
104
  &nbsp;&nbsp;&nbsp;╰ **Naming Convention** (naming-convention):\
105
105
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numMatches: 0, numBreak: 0, searchTimeMs: 0, processTimeMs: 0</code>\
106
106
  &nbsp;&nbsp;&nbsp;╰ **Network Functions** (network-functions):\
107
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0</code>\
107
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 1</code>\
108
108
  &nbsp;&nbsp;&nbsp;╰ **Dataframe Access Validation** (dataframe-access-validation):\
109
109
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 0</code>\
110
110
  &nbsp;&nbsp;&nbsp;╰ **Dead Code** (dead-code):\
111
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consideredNodes: 5, searchTimeMs: 1, processTimeMs: 0</code>\
111
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0</code>\
112
112
  &nbsp;&nbsp;&nbsp;╰ **Useless Loops** (useless-loop):\
113
113
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0</code>\
114
- &nbsp;&nbsp;&nbsp;╰ **Problematic eval** (problematic-eval):\
114
+ &nbsp;&nbsp;&nbsp;╰ **Problematic inputs** (problematic-inputs):\
115
115
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>searchTimeMs: 0, processTimeMs: 0</code>\
116
116
  &nbsp;&nbsp;&nbsp;╰ **Stop without call.=False argument** (stop-call):\
117
117
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consideredNodes: 0, searchTimeMs: 0, processTimeMs: 0</code>\
118
118
  &nbsp;&nbsp;&nbsp;╰ **Roxygen Arguments** (roxygen-arguments):\
119
119
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>searchTimeMs: 0, processTimeMs: 0</code>\
120
- _All queries together required ≈3 ms (1ms accuracy, total 3 ms)_
120
+ _All queries together required ≈2 ms (1ms accuracy, total 2 ms)_
121
121
 
122
122
  <details> <summary style="color:gray">Show Detailed Results as Json</summary>
123
123
 
124
- The analysis required _2.5 ms_ (including parsing and normalization and the query) within the generation environment.
124
+ The analysis required _2.2 ms_ (including parsing and normalization and the query) within the generation environment.
125
125
 
126
126
  In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR.
127
127
  Please consult the [Interface](https://github.com/flowr-analysis/flowr/wiki/Interface) wiki page for more information on how to get those.
@@ -138,7 +138,7 @@ It offers a wide variety of features, for example:
138
138
  ".meta": {
139
139
  "totalCalls": 0,
140
140
  "totalFunctionDefinitions": 0,
141
- "searchTimeMs": 1,
141
+ "searchTimeMs": 0,
142
142
  "processTimeMs": 0
143
143
  }
144
144
  },
@@ -161,7 +161,7 @@ It offers a wide variety of features, for example:
161
161
  "totalUnknown": 0,
162
162
  "totalWritesBeforeAlways": 0,
163
163
  "totalValid": 0,
164
- "searchTimeMs": 0,
164
+ "searchTimeMs": 1,
165
165
  "processTimeMs": 0
166
166
  }
167
167
  },
@@ -173,7 +173,7 @@ It offers a wide variety of features, for example:
173
173
  "callsWithAssignmentProducers": 0,
174
174
  "callsWithNonConstantProducers": 0,
175
175
  "callsWithOtherBranchProducers": 0,
176
- "searchTimeMs": 1,
176
+ "searchTimeMs": 0,
177
177
  "processTimeMs": 0
178
178
  }
179
179
  },
@@ -220,7 +220,7 @@ It offers a wide variety of features, for example:
220
220
  "totalCalls": 0,
221
221
  "totalFunctionDefinitions": 0,
222
222
  "searchTimeMs": 0,
223
- "processTimeMs": 0
223
+ "processTimeMs": 1
224
224
  }
225
225
  },
226
226
  "dataframe-access-validation": {
@@ -237,7 +237,7 @@ It offers a wide variety of features, for example:
237
237
  "results": [],
238
238
  ".meta": {
239
239
  "consideredNodes": 5,
240
- "searchTimeMs": 1,
240
+ "searchTimeMs": 0,
241
241
  "processTimeMs": 0
242
242
  }
243
243
  },
@@ -249,7 +249,7 @@ It offers a wide variety of features, for example:
249
249
  "processTimeMs": 0
250
250
  }
251
251
  },
252
- "problematic-eval": {
252
+ "problematic-inputs": {
253
253
  "results": [],
254
254
  ".meta": {
255
255
  "searchTimeMs": 0,
@@ -273,11 +273,11 @@ It offers a wide variety of features, for example:
273
273
  }
274
274
  },
275
275
  ".meta": {
276
- "timing": 3
276
+ "timing": 2
277
277
  }
278
278
  },
279
279
  ".meta": {
280
- "timing": 3
280
+ "timing": 2
281
281
  }
282
282
  }
283
283
  ```
@@ -342,7 +342,7 @@ It offers a wide variety of features, for example:
342
342
 
343
343
  ```shell
344
344
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
345
- flowR repl using flowR v2.10.2, R grammar v14 (tree-sitter engine)
345
+ flowR repl using flowR v2.10.3, R grammar v14 (tree-sitter engine)
346
346
  R> :query @static-slice (11@sum) file://test/testfiles/example.R
347
347
  ```
348
348
 
@@ -356,7 +356,7 @@ It offers a wide variety of features, for example:
356
356
  N <- 10
357
357
  for(i in 1:(N-1)) sum <- sum + i + w
358
358
  sum
359
- All queries together required ≈3 ms (1ms accuracy, total 3 ms)
359
+ All queries together required ≈2 ms (1ms accuracy, total 2 ms)
360
360
  ```
361
361
 
362
362
 
@@ -390,7 +390,7 @@ It offers a wide variety of features, for example:
390
390
 
391
391
 
392
392
  * 🚀 **fast call-graph, data-, and control-flow graphs**\
393
- Within just [<i><span title="This measurement is automatically fetched from the latest benchmark!">100.4 ms</span></i> (as of Apr 2, 2026)](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark),
393
+ Within just [<i><span title="This measurement is automatically fetched from the latest benchmark!">104.1 ms</span></i> (as of Apr 7, 2026)](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark),
394
394
  _flowR_ can analyze the data- and control-flow of the average real-world R&nbsp;script. See the [benchmarks](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark) for more information,
395
395
  and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/dataflow-graph) for more details on the [dataflow graphs](https://github.com/flowr-analysis/flowr/wiki/dataflow-graph) as well as [call graphs](https://github.com/flowr-analysis/flowr/wiki/dataflow-graph#perspectives-cg).
396
396
 
@@ -426,7 +426,7 @@ It offers a wide variety of features, for example:
426
426
 
427
427
  ```shell
428
428
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
429
- flowR repl using flowR v2.10.2, R grammar v14 (tree-sitter engine)
429
+ flowR repl using flowR v2.10.3, R grammar v14 (tree-sitter engine)
430
430
  R> :dataflow* test/testfiles/example.R
431
431
  ```
432
432
 
@@ -124,7 +124,7 @@ df[6, "value"]
124
124
  rule(knownParser, 'useless-loop', 'UselessLoopConfig', 'USELESS_LOOP', 'lint-useless-loop', 'for(i in c(1)) { print(i) }', tagTypes);
125
125
  rule(knownParser, 'stop-call', 'StopWithCallConfig', 'STOP_WITH_CALL_ARG', 'lint-stop-call', 'stop(42)', tagTypes);
126
126
  rule(knownParser, 'roxygen-arguments', 'RoxygenArgsConfig', 'ROXYGEN_ARGS', 'lint-roxygen-arguments', '#\' A function with two parameters, but only only one documented\n#\' @param a A variable\nf = function(a, b){return a;}', tagTypes);
127
- rule(knownParser, 'problematic-eval', 'ProblematicEvalConfig', 'PROBLEMATIC_EVAL', 'lint-problematic-eval', `
127
+ rule(knownParser, 'problematic-inputs', 'ProblematicInputsConfig', 'PROBLEMATIC_INPUTS', 'lint-problematic-inputs', `
128
128
  function(x) {
129
129
  eval(x)
130
130
  }
@@ -78,7 +78,7 @@ export declare const LintingRules: {
78
78
  readonly type: "assignment";
79
79
  readonly name: ".Random.seed";
80
80
  }];
81
- readonly randomnessConsumers: readonly ["jitter", "sample", "sample.int", "arima.sim", "kmeans", "princomp", "rcauchy", "rchisq", "rexp", "rgamma", "rgeom", "rlnorm", "rlogis", "rmultinom", "rnbinom", "rnorm", "rpois", "runif", "pointLabel", "some", "rbernoulli", "rdunif", "generateSeedVectors"];
81
+ readonly randomnessConsumers: readonly ["jitter", "sample", "sample.int", "arima.sim", "kmeans", "princomp", "rcauchy", "rchisq", "rexp", "rgamma", "rgeom", "rlnorm", "rlogis", "rmultinom", "rnbinom", "rnorm", "rpois", "runif", "pointLabel", "some", "rbernoulli", "rdunif", "generateSeedVectors", "rbeta", "rf", "rhyper", "rweibull", "rt", "rvonmises", "rwilcox", "rxor", "rhyper", "rmvnorm", "rsignrank", "randomForest", "permuted", "permute", "shuffle", "shuffleSet", "data_shuffle", "sample_frac", "sample_n", "slice_sample"];
82
82
  };
83
83
  readonly tags: readonly [import("./linter-tags").LintingRuleTag.Robustness, import("./linter-tags").LintingRuleTag.Reproducibility];
84
84
  readonly certainty: import("./linter-format").LintingRuleCertainty.BestEffort;
@@ -196,7 +196,7 @@ export declare const LintingRules: {
196
196
  readonly certainty: import("./linter-format").LintingRuleCertainty.BestEffort;
197
197
  readonly description: "Marks network functions that execute network operations, such as downloading files or making HTTP requests.";
198
198
  readonly defaultConfig: {
199
- readonly fns: readonly ["read.table", "read.csv", "read.csv2", "read.delim", "read.delim2", "readRDS", "download.file", "url", "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "content", "handle", "get_callback", "VERB", "fread", "gzcon", "readlines", "source", "load", "curl_download", "curl_fetch_memory", "getURL", "getForm", "read_html", "html_nodes", "html_text", "fromJSON", "read.xlsx", "drive_download", "drive_get", "s3read_using", "s3write_using", "storage_download", "AnnotationHub", "ExperimentHub"];
199
+ readonly fns: readonly ["read.table", "read.csv", "read.csv2", "read.delim", "read.delim2", "readRDS", "download.file", "url", "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "content", "handle", "get_callback", "VERB", "fread", "gzcon", "readlines", "readLines", "source", "load", "curl_download", "curl_fetch_memory", "getURL", "getForm", "read_html", "read_xml", "html_nodes", "html_text", "fromJSON", "read.xlsx", "drive_download", "drive_get", "s3read_using", "s3write_using", "storage_download", "AnnotationHub", "ExperimentHub", "scan", "socketConnection", "request", "curl"];
200
200
  readonly onlyTriggerWithArgument: RegExp;
201
201
  };
202
202
  };
@@ -281,28 +281,28 @@ export declare const LintingRules: {
281
281
  };
282
282
  };
283
283
  };
284
- readonly 'problematic-eval': {
285
- readonly createSearch: (config: import("./rules/problematic-eval").ProblematicEvalConfig) => import("../search/flowr-search-builder").FlowrSearchBuilder<"from-query", [], import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../search/flowr-search").FlowrSearchElements<import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../search/flowr-search").FlowrSearchElement<import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>[]>>;
286
- readonly processSearchResult: (elements: import("../search/flowr-search").FlowrSearchElements<import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../search/flowr-search").FlowrSearchElement<import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>[]>, _config: import("./rules/problematic-eval").ProblematicEvalConfig, data: {
284
+ readonly 'problematic-inputs': {
285
+ readonly createSearch: (config: import("./rules/problematic-inputs").ProblematicInputsConfig) => import("../search/flowr-search-builder").FlowrSearchBuilder<"from-query", [], import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../search/flowr-search").FlowrSearchElements<import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../search/flowr-search").FlowrSearchElement<import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>[]>>;
286
+ readonly processSearchResult: (elements: import("../search/flowr-search").FlowrSearchElements<import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../search/flowr-search").FlowrSearchElement<import("../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>[]>, _config: import("./rules/problematic-inputs").ProblematicInputsConfig, data: {
287
287
  normalize: import("../r-bridge/lang-4.x/ast/model/processing/decorate").NormalizedAst;
288
288
  dataflow: import("../dataflow/info").DataflowInformation;
289
289
  cfg: import("../control-flow/control-flow-graph").ControlFlowInformation;
290
290
  analyzer: import("../project/flowr-analyzer").ReadonlyFlowrAnalysisProvider;
291
291
  }) => Promise<{
292
- results: import("./rules/problematic-eval").ProblematicEvalResult[];
293
- ".meta": import("./rules/problematic-eval").ProblematicEvalMetadata;
292
+ results: import("./rules/problematic-inputs").ProblematicInputsResult[];
293
+ ".meta": import("./rules/problematic-inputs").ProblematicInputsMetadata;
294
294
  }>;
295
295
  readonly prettyPrint: {
296
- readonly query: (result: import("./rules/problematic-eval").ProblematicEvalResult) => string;
297
- readonly full: (result: import("./rules/problematic-eval").ProblematicEvalResult) => string;
296
+ readonly query: (result: import("./rules/problematic-inputs").ProblematicInputsResult) => string;
297
+ readonly full: (result: import("./rules/problematic-inputs").ProblematicInputsResult) => string;
298
298
  };
299
299
  readonly info: {
300
- readonly name: "Problematic eval";
301
- readonly description: "Detects uses of eval-like functions whose inputs are not statically constant. Prints the computed input-sources for the eval and flags usages that depend on non-constant/trusted inputs.";
300
+ readonly name: "Problematic inputs";
301
+ readonly description: "Detects uses of configured dynamic calls (e.g. eval, system) whose inputs are not statically constant. Prints the computed input-sources for the call and flags usages that depend on non-constant/trusted inputs.";
302
302
  readonly tags: readonly [import("./linter-tags").LintingRuleTag.Security, import("./linter-tags").LintingRuleTag.Smell, import("./linter-tags").LintingRuleTag.Readability, import("./linter-tags").LintingRuleTag.Performance];
303
303
  readonly certainty: import("./linter-format").LintingRuleCertainty.BestEffort;
304
304
  readonly defaultConfig: {
305
- readonly considerAsEval: "^eval$";
305
+ readonly consider: readonly ["^eval$", "^system$", "^system2$", "^shell$"];
306
306
  };
307
307
  };
308
308
  };
@@ -13,7 +13,7 @@ const useless_loop_1 = require("./rules/useless-loop");
13
13
  const network_functions_1 = require("./rules/network-functions");
14
14
  const stop_with_call_arg_1 = require("./rules/stop-with-call-arg");
15
15
  const roxygen_arguments_1 = require("./rules/roxygen-arguments");
16
- const problematic_eval_1 = require("./rules/problematic-eval");
16
+ const problematic_inputs_1 = require("./rules/problematic-inputs");
17
17
  /**
18
18
  * The registry of currently supported linting rules.
19
19
  * A linting rule can be executed on a dataflow pipeline result using {@link executeLintingRule}.
@@ -29,7 +29,7 @@ exports.LintingRules = {
29
29
  'dataframe-access-validation': dataframe_access_validation_1.DATA_FRAME_ACCESS_VALIDATION,
30
30
  'dead-code': dead_code_1.DEAD_CODE,
31
31
  'useless-loop': useless_loop_1.USELESS_LOOP,
32
- 'problematic-eval': problematic_eval_1.PROBLEMATIC_EVAL,
32
+ 'problematic-inputs': problematic_inputs_1.PROBLEMATIC_INPUTS,
33
33
  'stop-call': stop_with_call_arg_1.STOP_WITH_CALL_ARG,
34
34
  'roxygen-arguments': roxygen_arguments_1.ROXYGEN_ARGS
35
35
  };
@@ -31,7 +31,7 @@ export declare const NETWORK_FUNCTIONS: {
31
31
  readonly certainty: LintingRuleCertainty.BestEffort;
32
32
  readonly description: "Marks network functions that execute network operations, such as downloading files or making HTTP requests.";
33
33
  readonly defaultConfig: {
34
- readonly fns: readonly ["read.table", "read.csv", "read.csv2", "read.delim", "read.delim2", "readRDS", "download.file", "url", "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "content", "handle", "get_callback", "VERB", "fread", "gzcon", "readlines", "source", "load", "curl_download", "curl_fetch_memory", "getURL", "getForm", "read_html", "html_nodes", "html_text", "fromJSON", "read.xlsx", "drive_download", "drive_get", "s3read_using", "s3write_using", "storage_download", "AnnotationHub", "ExperimentHub"];
34
+ readonly fns: readonly ["read.table", "read.csv", "read.csv2", "read.delim", "read.delim2", "readRDS", "download.file", "url", "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "content", "handle", "get_callback", "VERB", "fread", "gzcon", "readlines", "readLines", "source", "load", "curl_download", "curl_fetch_memory", "getURL", "getForm", "read_html", "read_xml", "html_nodes", "html_text", "fromJSON", "read.xlsx", "drive_download", "drive_get", "s3read_using", "s3write_using", "storage_download", "AnnotationHub", "ExperimentHub", "scan", "socketConnection", "request", "curl"];
35
35
  readonly onlyTriggerWithArgument: RegExp;
36
36
  };
37
37
  };
@@ -29,8 +29,14 @@ exports.NETWORK_FUNCTIONS = {
29
29
  certainty: linter_format_1.LintingRuleCertainty.BestEffort,
30
30
  description: 'Marks network functions that execute network operations, such as downloading files or making HTTP requests.',
31
31
  defaultConfig: {
32
- fns: ['read.table', 'read.csv', 'read.csv2', 'read.delim', 'read.delim2', 'readRDS', 'download.file', 'url', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'content', 'handle', 'get_callback', 'VERB', 'fread', 'gzcon', 'readlines', 'source', 'load', 'curl_download', 'curl_fetch_memory', 'getURL', 'getForm', 'read_html', 'html_nodes', 'html_text', 'fromJSON', 'read.xlsx', 'drive_download', 'drive_get', 's3read_using', 's3write_using', 'storage_download', 'AnnotationHub', 'ExperimentHub'],
33
- onlyTriggerWithArgument: /^(https?|ftps?|file):\/\//
32
+ fns: [
33
+ 'read.table', 'read.csv', 'read.csv2', 'read.delim', 'read.delim2', 'readRDS', 'download.file', 'url', 'GET', 'POST', 'PUT',
34
+ 'DELETE', 'PATCH', 'HEAD', 'content', 'handle', 'get_callback', 'VERB', 'fread', 'gzcon', 'readlines', 'readLines', 'source', 'load', 'curl_download',
35
+ 'curl_fetch_memory', 'getURL', 'getForm', 'read_html', 'read_xml', 'html_nodes', 'html_text', 'fromJSON', 'read.xlsx', 'drive_download', 'drive_get',
36
+ 's3read_using', 's3write_using', 'storage_download', 'AnnotationHub', 'ExperimentHub', 'scan',
37
+ 'socketConnection', 'request', 'curl'
38
+ ],
39
+ onlyTriggerWithArgument: /^(https?|ftps?):\/\//
34
40
  }
35
41
  }
36
42
  };
@@ -6,39 +6,37 @@ import type { InputSources } from '../../queries/catalog/input-sources-query/sim
6
6
  /**
7
7
  * Describes a linting result for a problematic eval usage, including the location of the eval call and the computed input sources that lead to it.
8
8
  */
9
- export interface ProblematicEvalResult extends LintingResult {
9
+ export interface ProblematicInputsResult extends LintingResult {
10
+ name: string;
10
11
  loc: SourceLocation;
11
12
  sources: InputSources;
12
13
  }
13
- export interface ProblematicEvalConfig extends MergeableRecord {
14
- /**
15
- * All calls that should be considered to be valid eval entry points, this will be interpreted as a Regex!
16
- */
17
- considerAsEval: string;
14
+ export interface ProblematicInputsConfig extends MergeableRecord {
15
+ consider?: string | string[];
18
16
  }
19
- export type ProblematicEvalMetadata = MergeableRecord;
20
- export declare const PROBLEMATIC_EVAL: {
21
- readonly createSearch: (config: ProblematicEvalConfig) => import("../../search/flowr-search-builder").FlowrSearchBuilder<"from-query", [], import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../../search/flowr-search").FlowrSearchElements<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../../search/flowr-search").FlowrSearchElement<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>[]>>;
22
- readonly processSearchResult: (elements: import("../../search/flowr-search").FlowrSearchElements<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../../search/flowr-search").FlowrSearchElement<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>[]>, _config: ProblematicEvalConfig, data: {
17
+ export type ProblematicInputsMetadata = MergeableRecord;
18
+ export declare const PROBLEMATIC_INPUTS: {
19
+ readonly createSearch: (config: ProblematicInputsConfig) => import("../../search/flowr-search-builder").FlowrSearchBuilder<"from-query", [], import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../../search/flowr-search").FlowrSearchElements<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../../search/flowr-search").FlowrSearchElement<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>[]>>;
20
+ readonly processSearchResult: (elements: import("../../search/flowr-search").FlowrSearchElements<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../../search/flowr-search").FlowrSearchElement<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>[]>, _config: ProblematicInputsConfig, data: {
23
21
  normalize: import("../../r-bridge/lang-4.x/ast/model/processing/decorate").NormalizedAst;
24
22
  dataflow: import("../../dataflow/info").DataflowInformation;
25
23
  cfg: import("../../control-flow/control-flow-graph").ControlFlowInformation;
26
24
  analyzer: import("../../project/flowr-analyzer").ReadonlyFlowrAnalysisProvider;
27
25
  }) => Promise<{
28
- results: ProblematicEvalResult[];
29
- ".meta": ProblematicEvalMetadata;
26
+ results: ProblematicInputsResult[];
27
+ ".meta": ProblematicInputsMetadata;
30
28
  }>;
31
29
  readonly prettyPrint: {
32
- readonly query: (result: ProblematicEvalResult) => string;
33
- readonly full: (result: ProblematicEvalResult) => string;
30
+ readonly query: (result: ProblematicInputsResult) => string;
31
+ readonly full: (result: ProblematicInputsResult) => string;
34
32
  };
35
33
  readonly info: {
36
- readonly name: "Problematic eval";
37
- readonly description: "Detects uses of eval-like functions whose inputs are not statically constant. Prints the computed input-sources for the eval and flags usages that depend on non-constant/trusted inputs.";
34
+ readonly name: "Problematic inputs";
35
+ readonly description: "Detects uses of configured dynamic calls (e.g. eval, system) whose inputs are not statically constant. Prints the computed input-sources for the call and flags usages that depend on non-constant/trusted inputs.";
38
36
  readonly tags: readonly [LintingRuleTag.Security, LintingRuleTag.Smell, LintingRuleTag.Readability, LintingRuleTag.Performance];
39
37
  readonly certainty: LintingRuleCertainty.BestEffort;
40
38
  readonly defaultConfig: {
41
- readonly considerAsEval: "^eval$";
39
+ readonly consider: readonly ["^eval$", "^system$", "^system2$", "^shell$"];
42
40
  };
43
41
  };
44
42
  };
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PROBLEMATIC_INPUTS = void 0;
4
+ const linter_format_1 = require("../linter-format");
5
+ const flowr_search_builder_1 = require("../../search/flowr-search-builder");
6
+ const range_1 = require("../../util/range");
7
+ const linter_tags_1 = require("../linter-tags");
8
+ const simple_input_classifier_1 = require("../../queries/catalog/input-sources-query/simple-input-classifier");
9
+ const parse_1 = require("../../slicing/criterion/parse");
10
+ const defaultConsider = ['^eval$', '^system$', '^system2$', '^shell$'];
11
+ function normalizeConsider(cfg) {
12
+ if (cfg?.consider === undefined) {
13
+ return Array.from(defaultConsider, s => new RegExp(s));
14
+ }
15
+ if (Array.isArray(cfg.consider)) {
16
+ const arr = cfg.consider.length === 0 ? Array.from(defaultConsider) : cfg.consider;
17
+ // deduplicate while preserving order
18
+ return Array.from(new Set(arr), s => new RegExp(s));
19
+ }
20
+ return [new RegExp(cfg.consider)];
21
+ }
22
+ /**
23
+ * Format a list of input sources either as a single-line string (inline) or a block.
24
+ * - inline: returns a semicolon-separated single-line summary
25
+ * - block: returns an array of lines (to be joined with newlines by the caller)
26
+ */
27
+ function formatInputSources(inputs, inline = true) {
28
+ if (!inputs || inputs.length === 0) {
29
+ return inline ? '' : [];
30
+ }
31
+ const formatOne = (s, inlineMode) => {
32
+ const typeStr = '[' + s.types.join(',') + ']';
33
+ const cdsStr = s.cds ? ', cds: [' + s.cds.join(',') + ']' : '';
34
+ return inlineMode
35
+ ? `${s.id} (type: ${typeStr}, trace: ${s.trace}${cdsStr})`
36
+ : `- ${s.id}: type=${typeStr}, trace=${s.trace}${cdsStr}`;
37
+ };
38
+ if (inline) {
39
+ return inputs.map(s => formatOne(s, true)).join('; ');
40
+ }
41
+ return inputs.map(s => formatOne(s, false));
42
+ }
43
+ // small helpers to keep checks readable
44
+ function hasUnknownSource(sources) {
45
+ return sources.some(s => s.types.includes(simple_input_classifier_1.InputType.Unknown));
46
+ }
47
+ function isProblematicForAllowed(sources, allowed) {
48
+ return sources.some(s => s.types.some(t => !allowed.includes(t)));
49
+ }
50
+ exports.PROBLEMATIC_INPUTS = {
51
+ createSearch: config => {
52
+ const cfg = config;
53
+ const considerArr = normalizeConsider(cfg);
54
+ const queries = considerArr.map((name, i) => ({
55
+ type: 'call-context',
56
+ callName: name,
57
+ callNameExact: false,
58
+ subkind: `fn-${i}`
59
+ }));
60
+ return flowr_search_builder_1.Q.fromQuery(...queries);
61
+ },
62
+ processSearchResult: async (elements, _config, data) => {
63
+ const results = [];
64
+ const defaultAccept = [simple_input_classifier_1.InputType.Constant, simple_input_classifier_1.InputType.DerivedConstant];
65
+ for (const element of elements.getElements()) {
66
+ const nid = element.node.info.id;
67
+ const criterion = parse_1.SlicingCriterion.fromId(nid);
68
+ const q = { type: 'input-sources', criterion };
69
+ const all = await data.analyzer.query([q]);
70
+ const inputSourcesResult = all['input-sources'];
71
+ const sources = inputSourcesResult?.results?.[criterion] ?? [];
72
+ if (isProblematicForAllowed(sources, defaultAccept)) {
73
+ const certainty = hasUnknownSource(sources) ? linter_format_1.LintingResultCertainty.Uncertain : linter_format_1.LintingResultCertainty.Certain;
74
+ results.push({
75
+ involvedId: nid,
76
+ certainty,
77
+ loc: range_1.SourceLocation.fromNode(element.node) ?? range_1.SourceLocation.invalid(),
78
+ name: element.node.lexeme ?? '',
79
+ sources
80
+ });
81
+ }
82
+ }
83
+ return {
84
+ results,
85
+ '.meta': {}
86
+ };
87
+ },
88
+ /* helper to format input sources for pretty printing */
89
+ prettyPrint: {
90
+ [linter_format_1.LintingPrettyPrintContext.Query]: result => {
91
+ const inputs = result.sources ?? [];
92
+ const srcStr = formatInputSources(inputs, true);
93
+ return 'Use of configured dynamic call at ' + range_1.SourceLocation.format(result.loc) + (srcStr ? '; inputs: ' + srcStr : '');
94
+ },
95
+ [linter_format_1.LintingPrettyPrintContext.Full]: result => {
96
+ const inputs = result.sources ?? [];
97
+ const srcLines = formatInputSources(inputs, false);
98
+ return 'Use of configured dynamic call at ' + range_1.SourceLocation.format(result.loc) + ' is potentially problematic' + (srcLines.length ? '\nInputs:\n' + srcLines.join('\n') : '');
99
+ }
100
+ },
101
+ info: {
102
+ name: 'Problematic inputs',
103
+ description: 'Detects uses of configured dynamic calls (e.g. eval, system) whose inputs are not statically constant. Prints the computed input-sources for the call and flags usages that depend on non-constant/trusted inputs.',
104
+ tags: [linter_tags_1.LintingRuleTag.Security, linter_tags_1.LintingRuleTag.Smell, linter_tags_1.LintingRuleTag.Readability, linter_tags_1.LintingRuleTag.Performance],
105
+ certainty: linter_format_1.LintingRuleCertainty.BestEffort,
106
+ defaultConfig: {
107
+ consider: defaultConsider
108
+ }
109
+ }
110
+ };
111
+ //# sourceMappingURL=problematic-inputs.js.map
@@ -54,7 +54,7 @@ export declare const SEEDED_RANDOMNESS: {
54
54
  readonly type: "assignment";
55
55
  readonly name: ".Random.seed";
56
56
  }];
57
- readonly randomnessConsumers: readonly ["jitter", "sample", "sample.int", "arima.sim", "kmeans", "princomp", "rcauchy", "rchisq", "rexp", "rgamma", "rgeom", "rlnorm", "rlogis", "rmultinom", "rnbinom", "rnorm", "rpois", "runif", "pointLabel", "some", "rbernoulli", "rdunif", "generateSeedVectors"];
57
+ readonly randomnessConsumers: readonly ["jitter", "sample", "sample.int", "arima.sim", "kmeans", "princomp", "rcauchy", "rchisq", "rexp", "rgamma", "rgeom", "rlnorm", "rlogis", "rmultinom", "rnbinom", "rnorm", "rpois", "runif", "pointLabel", "some", "rbernoulli", "rdunif", "generateSeedVectors", "rbeta", "rf", "rhyper", "rweibull", "rt", "rvonmises", "rwilcox", "rxor", "rhyper", "rmvnorm", "rsignrank", "randomForest", "permuted", "permute", "shuffle", "shuffleSet", "data_shuffle", "sample_frac", "sample_n", "slice_sample"];
58
58
  };
59
59
  readonly tags: readonly [LintingRuleTag.Robustness, LintingRuleTag.Reproducibility];
60
60
  readonly certainty: LintingRuleCertainty.BestEffort;
@@ -126,7 +126,14 @@ exports.SEEDED_RANDOMNESS = {
126
126
  info: {
127
127
  defaultConfig: {
128
128
  randomnessProducers: [{ type: 'function', name: 'set.seed' }, { type: 'assignment', name: '.Random.seed' }],
129
- randomnessConsumers: ['jitter', 'sample', 'sample.int', 'arima.sim', 'kmeans', 'princomp', 'rcauchy', 'rchisq', 'rexp', 'rgamma', 'rgeom', 'rlnorm', 'rlogis', 'rmultinom', 'rnbinom', 'rnorm', 'rpois', 'runif', 'pointLabel', 'some', 'rbernoulli', 'rdunif', 'generateSeedVectors'],
129
+ randomnessConsumers: [
130
+ 'jitter', 'sample', 'sample.int', 'arima.sim', 'kmeans', 'princomp', 'rcauchy', 'rchisq', 'rexp',
131
+ 'rgamma', 'rgeom', 'rlnorm', 'rlogis', 'rmultinom', 'rnbinom', 'rnorm', 'rpois', 'runif', 'pointLabel',
132
+ 'some', 'rbernoulli', 'rdunif', 'generateSeedVectors',
133
+ 'rbeta', 'rf', 'rhyper', 'rweibull', 'rt', 'rvonmises', 'rwilcox', 'rxor', 'rhyper', 'rmvnorm',
134
+ 'rsignrank', 'randomForest',
135
+ 'permuted', 'permute', 'shuffle', 'shuffleSet', 'data_shuffle', 'sample_frac', 'sample_n', 'slice_sample',
136
+ ],
130
137
  },
131
138
  tags: [linter_tags_1.LintingRuleTag.Robustness, linter_tags_1.LintingRuleTag.Reproducibility],
132
139
  // only finds proper randomness producers and consumers due to its config, but will not find all producers/consumers since not all existing deprecated functions will be in the config
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.10.3",
3
+ "version": "2.10.4",
4
4
  "description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "repository": {
@@ -167,7 +167,7 @@
167
167
  "devDependencies": {
168
168
  "@commitlint/cli": "^20.5.0",
169
169
  "@commitlint/config-angular": "^20.5.0",
170
- "@eagleoutice/eslint-config-flowr": "^1.0.36",
170
+ "@eagleoutice/eslint-config-flowr": "^1.0.40",
171
171
  "@eslint/eslintrc": "^3.3.3",
172
172
  "@eslint/js": "^9.39.2",
173
173
  "@j-ulrich/release-it-regex-bumper": "^5.3.0",
@@ -77,14 +77,14 @@ function promoteQueryCallNames(queries) {
77
77
  ...q.fileFilter,
78
78
  filter: promoteCallName(q.fileFilter.filter)
79
79
  },
80
- linkTo: Array.isArray(q.linkTo) ? q.linkTo.map(l => ({
80
+ linkTo: q.linkTo ? Array.isArray(q.linkTo) ? q.linkTo.map(l => ({
81
81
  ...l,
82
82
  callName: promoteCallName(l.callName)
83
83
  })) : {
84
84
  ...q.linkTo,
85
85
  /* we have to add another promotion layer whenever we add something without this call name */
86
86
  callName: promoteCallName(q.linkTo.callName)
87
- }
87
+ } : undefined
88
88
  };
89
89
  }
90
90
  else {
@@ -88,7 +88,7 @@ export type LinkTo<CallName extends CallNameTypes = CallNameTypes, AttachLinkInf
88
88
  attachLinkInfo?: AttachLinkInfo;
89
89
  };
90
90
  export interface SubCallContextQueryFormat<CallName extends CallNameTypes = CallNameTypes, AttachLinkInfo = NoInfo> extends DefaultCallContextQueryFormat<CallName> {
91
- readonly linkTo: LinkTo<CallName, AttachLinkInfo> | LinkTo<CallName, AttachLinkInfo>[];
91
+ readonly linkTo?: LinkTo<CallName, AttachLinkInfo> | LinkTo<CallName, AttachLinkInfo>[];
92
92
  }
93
93
  export interface CallContextQuerySubKindResult {
94
94
  /** The id of the call vertex identified within the supplied dataflow graph */
@@ -29,8 +29,7 @@ exports.CallContextQueryDefinition = {
29
29
  executor: call_context_query_executor_1.executeCallContextQueries,
30
30
  asciiSummarizer: async (formatter, analyzer, queryResults, result) => {
31
31
  const out = queryResults;
32
- result.push(`Query: ${(0, ansi_1.bold)('call-context', formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
33
- result.push((0, query_print_1.asciiCallContext)(formatter, out, (await analyzer.normalize()).idMap));
32
+ result.push(`Query: ${(0, ansi_1.bold)('call-context', formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`, (0, query_print_1.asciiCallContext)(formatter, out, (await analyzer.normalize()).idMap));
34
33
  return true;
35
34
  },
36
35
  schema: joi_1.default.object({
@@ -108,5 +108,11 @@ exports.ReadFunctions = [
108
108
  { package: 'rpolars', name: 'pl_scan_ipc', argIdx: 0, argName: 'source', resolveValue: true, ignoreIf: 'arg-missing' },
109
109
  { package: 'rpolars', name: 'pl_scan_ndjson', argIdx: 0, argName: 'source', resolveValue: true, ignoreIf: 'arg-missing' },
110
110
  { package: 'rpolars', name: 'pl_scan_parquet', argIdx: 0, argName: 'source', resolveValue: true, ignoreIf: 'arg-missing' },
111
+ { package: 'rio', name: 'import', argIdx: 0, argName: 'file', resolveValue: true },
112
+ { package: 'rio', name: 'import_list', argIdx: 0, argName: 'file', resolveValue: true },
113
+ { package: 'openxlsx', name: 'read.xlsx', argIdx: 0, argName: 'file', resolveValue: true },
114
+ { package: 'openxlsx', name: 'loadWorkbook', argIdx: 0, argName: 'file', resolveValue: true },
115
+ { package: 'readODS', name: 'read_ods', argIdx: 0, argName: 'path', resolveValue: true },
116
+ { package: 'vroom', name: 'vroom', argIdx: 0, argName: 'file', resolveValue: true },
111
117
  ];
112
118
  //# sourceMappingURL=read-functions.js.map
@@ -115,6 +115,13 @@ exports.WriteFunctions = [
115
115
  { package: 'rpolars', name: 'write_csv', argIdx: 0, argName: 'file', resolveValue: true, ignoreIf: 'arg-missing' },
116
116
  { package: 'rpolars', name: 'write_ndjson', argIdx: 0, argName: 'file', resolveValue: true, ignoreIf: 'arg-missing' },
117
117
  { package: 'rpolars', name: 'write_parquet', argIdx: 0, argName: 'file', resolveValue: true, ignoreIf: 'arg-missing' },
118
+ { package: 'data.table', name: 'fwrite', argIdx: 1, argName: 'file', resolveValue: true },
119
+ { package: 'writexl', name: 'write_xlsx', argIdx: 1, argName: 'path', resolveValue: true },
120
+ { package: 'openxlsx', name: 'write.xlsx', argIdx: 1, argName: 'file', resolveValue: true },
121
+ { package: 'vroom', name: 'vroom_write', argIdx: 1, argName: 'file', resolveValue: true },
122
+ { package: 'vroom', name: 'vroom_write_lines', argIdx: 1, argName: 'file', resolveValue: true },
123
+ { package: 'rio', name: 'export', argIdx: 1, argName: 'file', resolveValue: true },
124
+ { package: 'rio', name: 'export_list', argIdx: 1, argName: 'file', resolveValue: true },
118
125
  { package: 'magick', name: 'image_write', argIdx: 1, argName: 'path', resolveValue: true, ignoreIf: 'arg-missing' },
119
126
  ];
120
127
  //# sourceMappingURL=write-functions.js.map
@@ -28,6 +28,7 @@ async function executeInputSourcesQuery({ analyzer }, queries) {
28
28
  const fdef = r_function_definition_1.RFunctionDefinition.rootFunctionDefinition(provenanceNode, nast.idMap);
29
29
  const provenance = df_helper_1.Dataflow.provenanceGraph(criterionId, df.graph, fdef ? model_1.RNode.collectAllIds(fdef) : undefined);
30
30
  results[key] = (0, simple_input_classifier_1.classifyInput)(criterionId, provenance, {
31
+ fullDfg: df.graph,
31
32
  networkFns: query.config?.networkFns ?? network_functions_1.NETWORK_FUNCTIONS.info.defaultConfig.fns,
32
33
  randomFns: query.config?.randomFns ?? seeded_randomness_1.SEEDED_RANDOMNESS.info.defaultConfig.randomnessConsumers,
33
34
  pureFns: query.config?.pureFns ?? ['paste', 'paste0', 'parse', '+', '-', '*',
@@ -43,17 +44,38 @@ async function executeInputSourcesQuery({ analyzer }, queries) {
43
44
  'min', 'max', 'range', 'sum', 'prod', 'mean', 'median', 'var', 'sd',
44
45
  'head', 'tail', 'seq', 'rep',
45
46
  'apply', 'lapply', 'sapply', 'vapply', 'tapply',
46
- 'matrix', 'array', 'substitute', 'quote', 'bquote', 'enquote', 'enexpr', 'enexprs', 'enquo', 'enquos',
47
- 'expression', 'call', 'as.call', 'as.expression',
47
+ 'matrix', 'array',
48
48
  'rownames', 'colnames',
49
49
  'list.files', 'tolower', 'toupper', 'printf',
50
50
  '<-', '->', '=', '<<-', '->>', 'assign', 'get',
51
51
  '[', '[[', '$', 'length<-', 'dim<-', 'names<-', 'colnames<-', 'rownames<-',
52
- 'as.character', 'as.numeric', 'as.logical', 'as.list', 'as.data.frame', 'as.matrix', 'as.array',
52
+ 'as.character', 'as.numeric', 'as.logical', 'as.raw', 'as.list', 'as.data.frame', 'as.matrix', 'as.array',
53
53
  'identity', 'invisible', 'return', 'force', 'missing',
54
- 'print', 'cat', 'message', 'warning', 'stop'
54
+ 'print', 'cat', 'message', 'warning', 'stop',
55
+ 'format', 'sprintf', 'formatC',
56
+ 'is.na', 'is.null', 'is.numeric', 'is.character',
57
+ 'which', 'match', 'order', 'sort', 'unique', 'duplicated', 'na.omit',
58
+ 'grep', 'grepl', 'sub', 'gsub', 'regexpr', 'gregexpr', 'regexec', 'regmatches',
59
+ 'as.integer', 'as.double', 'as.complex',
60
+ 'trimws', 'seq_len', 'seq_along', 'rep.int',
61
+ 'pmin', 'pmax', 'cumsum', 'cumprod', 'cummax', 'cummin', 'diff', 'signif',
62
+ 'table', 'prop.table', 'xtabs',
63
+ 'rbind', 'cbind', 't', 'crossprod', 'tcrossprod',
64
+ 'colSums', 'rowSums', 'colMeans', 'rowMeans',
65
+ 'solve', 'det', 'eigen',
66
+ 'is.factor', 'is.logical', 'is.vector', 'is.matrix', 'is.data.frame',
67
+ ],
68
+ readFileFns: query.config?.readFileFns ?? read_functions_1.ReadFunctions.map(f => f.name),
69
+ systemFns: query.config?.systemFns ?? ['system', 'system2', 'pipe', 'shell', 'shell.exec'],
70
+ ffiFns: query.config?.ffiFns ?? ['.C', '.Call', '.Fortran', '.External', 'dyn.load', 'sourceCpp', 'getNativeSymbolInfo'],
71
+ langFns: query.config?.langFns ?? [
72
+ 'substitute', 'quote', 'bquote', 'enquote',
73
+ 'enexpr', 'enexprs', 'enquo', 'enquos',
74
+ 'expression', 'call', 'as.call', 'as.expression',
75
+ 'as.name', 'as.symbol', 'alist', 'as.language', 'evalq',
76
+ 'expr', 'quo', 'enexpr', 'ensym', 'ensyms'
55
77
  ],
56
- readFileFns: query.config?.readFileFns ?? read_functions_1.ReadFunctions.map(f => f.name)
78
+ optionsFns: query.config?.optionsFns ?? ['options', 'getOption', 'Sys.getenv']
57
79
  });
58
80
  }
59
81
  return {
@@ -29,10 +29,10 @@ exports.InputSourcesDefinition = {
29
29
  const nast = (await analyzer.normalize()).idMap;
30
30
  for (const [key, sources] of Object.entries(out.results)) {
31
31
  result.push(` ╰ Input Sources for ${key}`);
32
- for (const { id, trace, type } of sources) {
32
+ for (const { id, trace, types } of sources) {
33
33
  const kNode = nast.get(id);
34
34
  const kLoc = kNode ? range_1.SourceLocation.format(range_1.SourceLocation.fromNode(kNode)) : 'unknown location';
35
- result.push(` ╰ ${kLoc} (id: ${id}), type: ${JSON.stringify(type)}, trace: ${trace}`);
35
+ result.push(` ╰ ${kLoc} (id: ${id}), type: ${JSON.stringify(types)}, trace: ${trace}`);
36
36
  }
37
37
  }
38
38
  return true;
@@ -42,11 +42,14 @@ exports.InputSourcesDefinition = {
42
42
  type: joi_1.default.string().valid('input-sources').required().description('The type of the query.'),
43
43
  criterion: joi_1.default.string().required().description('The slicing criterion to use.'),
44
44
  config: joi_1.default.object({
45
- networkFunctions: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that fetch data from the network.'),
46
- randomnessConsumers: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that consume randomness.'),
47
- randomnessProducers: joi_1.default.array().items(joi_1.default.object({ type: joi_1.default.string().valid('function', 'assignment'), name: joi_1.default.string().required() })).optional().description('Functions or assignments that produce randomness seeds.'),
48
- configurableFunctions: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that read configuration (options/env).'),
49
- pureFunctions: joi_1.default.array().items(joi_1.default.string()).optional().description('Deterministic functions that keep constant inputs constant.'),
45
+ pureFns: joi_1.default.array().items(joi_1.default.string()).optional().description('Deterministic/pure functions: functions that preserve constantness of their inputs (e.g., arithmetic, parse).'),
46
+ networkFns: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that fetch data from the network (e.g., download.file, url connections).'),
47
+ randomFns: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that produce randomness (e.g., runif, rnorm).'),
48
+ readFileFns: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that read from the filesystem and produce data (e.g., read.csv, readRDS).'),
49
+ systemFns: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that execute system commands (e.g., system, system2, shell, pipe).'),
50
+ ffiFns: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that call native code via the R FFI (.C, .Call, .Fortran, .External, dyn.load).'),
51
+ langFns: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that produce language objects (e.g., substitute, quote, bquote, expression).'),
52
+ optionsFns: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that access or set global options (e.g., options, getOption).'),
50
53
  }).optional()
51
54
  }).description('Input Sources query definition'),
52
55
  flattenInvolvedNodes: (queryResults) => {
@@ -8,13 +8,13 @@ import { Identifier } from '../../../dataflow/environments/identifier';
8
8
  * joining differing lattice elements.
9
9
  *
10
10
  *```
11
- * [ Unknown ]
12
- * / / | \ \
13
- *[Param] [File] [Net] [Rand] [Scope]
14
- * \ \ | / /
15
- * [ DerivedConstant ]
16
- * |
17
- * [ Constant ]
11
+ * [ Unknown ]
12
+ * |
13
+ * [Param] [File] [Net], ...
14
+ * |
15
+ * [ DerivedConstant ]
16
+ * |
17
+ * [ Constant ]
18
18
  *```
19
19
  *
20
20
  */
@@ -23,6 +23,14 @@ export declare enum InputType {
23
23
  File = "file",
24
24
  Network = "net",
25
25
  Random = "rand",
26
+ /** Calls to system/system2 and similar */
27
+ System = "system",
28
+ /** Calls to .C / Fortran interfaces */
29
+ Ffi = "ffi",
30
+ /** Language objects (quote/substitute/etc.) */
31
+ Lang = "lang",
32
+ /** Global options / option accessors (options, getOption) */
33
+ Options = "options",
26
34
  Constant = "const",
27
35
  /** Read from environment/call scope */
28
36
  Scope = "scope",
@@ -46,7 +54,7 @@ export declare enum InputTraceType {
46
54
  */
47
55
  export interface InputSource extends MergeableRecord {
48
56
  id: NodeId;
49
- type: InputType[];
57
+ types: InputType[];
50
58
  trace: InputTraceType;
51
59
  /** if the trace is affected by control dependencies, they are classified too, this is a duplicate free array */
52
60
  cds?: InputType[];
@@ -81,6 +89,26 @@ export interface InputClassifierConfig extends MergeableRecord {
81
89
  * Functions that read from the file system
82
90
  */
83
91
  readFileFns: readonly InputClassifierFunctionIdentifier[];
92
+ /**
93
+ * Functions that call system utilities (system/system2)
94
+ */
95
+ systemFns?: readonly InputClassifierFunctionIdentifier[];
96
+ /**
97
+ * Functions that call native code via .C/.Fortran interfaces
98
+ */
99
+ ffiFns?: readonly InputClassifierFunctionIdentifier[];
100
+ /**
101
+ * Functions that produce language objects such as quote/substitute
102
+ */
103
+ langFns?: readonly InputClassifierFunctionIdentifier[];
104
+ /**
105
+ * Functions that access or set global options
106
+ */
107
+ optionsFns?: readonly InputClassifierFunctionIdentifier[];
108
+ /**
109
+ * For the scope escape analysis, pass on the full, non-reduced DFG here
110
+ */
111
+ fullDfg?: DataflowGraph;
84
112
  }
85
113
  /**
86
114
  * Takes the given id which is expected to either be:
@@ -6,6 +6,7 @@ const graph_1 = require("../../../dataflow/graph/graph");
6
6
  const objects_1 = require("../../../util/objects");
7
7
  const vertex_1 = require("../../../dataflow/graph/vertex");
8
8
  const df_helper_1 = require("../../../dataflow/graph/df-helper");
9
+ const edge_1 = require("../../../dataflow/graph/edge");
9
10
  const identifier_1 = require("../../../dataflow/environments/identifier");
10
11
  const assert_1 = require("../../../util/assert");
11
12
  const arrays_1 = require("../../../util/collections/arrays");
@@ -18,16 +19,20 @@ class InputClassifier {
18
19
  this.dfg = dfg;
19
20
  this.config = config;
20
21
  }
22
+ isDefinedByOnCall(id) {
23
+ const out = (this.config.fullDfg ?? this.dfg).outgoingEdges(id) ?? new Map();
24
+ return out.values().some(e => edge_1.DfEdge.includesType(e, edge_1.EdgeType.DefinedByOnCall));
25
+ }
21
26
  classifyEntry(vertex) {
22
27
  const cached = this.cache.get(vertex.id);
23
28
  if (cached) {
24
29
  return cached;
25
30
  }
26
31
  // insert temporary unknown to break cycles
27
- this.cache.set(vertex.id, { id: vertex.id, type: [InputType.Unknown], trace: InputTraceType.Unknown });
32
+ this.cache.set(vertex.id, { id: vertex.id, types: [InputType.Unknown], trace: InputTraceType.Unknown });
28
33
  switch (vertex.tag) {
29
34
  case vertex_1.VertexType.Value:
30
- return this.classifyCdsAndReturn(vertex, { id: vertex.id, type: [InputType.Constant], trace: InputTraceType.Unknown });
35
+ return this.classifyCdsAndReturn(vertex, { id: vertex.id, types: [InputType.Constant], trace: InputTraceType.Unknown });
31
36
  case vertex_1.VertexType.FunctionCall:
32
37
  return this.classifyFunctionCall(vertex);
33
38
  case vertex_1.VertexType.VariableDefinition:
@@ -35,7 +40,7 @@ class InputClassifier {
35
40
  case vertex_1.VertexType.Use:
36
41
  return this.classifyVariable(vertex);
37
42
  default:
38
- return this.classifyCdsAndReturn(vertex, { id: vertex.id, type: [InputType.Unknown], trace: InputTraceType.Unknown });
43
+ return this.classifyCdsAndReturn(vertex, { id: vertex.id, types: [InputType.Unknown], trace: InputTraceType.Unknown });
39
44
  }
40
45
  }
41
46
  classifyFunctionCall(call) {
@@ -59,17 +64,29 @@ class InputClassifier {
59
64
  }
60
65
  if (!matchesList(call, this.config.pureFns)) {
61
66
  if (matchesList(call, this.config.readFileFns)) {
62
- return this.classifyCdsAndReturn(call, { id: call.id, type: [InputType.File], trace: InputTraceType.Unknown });
67
+ return this.classifyCdsAndReturn(call, { id: call.id, types: [InputType.File], trace: InputTraceType.Unknown });
63
68
  }
64
69
  else if (matchesList(call, this.config.networkFns)) {
65
- return this.classifyCdsAndReturn(call, { id: call.id, type: [InputType.Network], trace: InputTraceType.Unknown });
70
+ return this.classifyCdsAndReturn(call, { id: call.id, types: [InputType.Network], trace: InputTraceType.Unknown });
66
71
  }
67
72
  else if (matchesList(call, this.config.randomFns)) {
68
- return this.classifyCdsAndReturn(call, { id: call.id, type: [InputType.Random], trace: InputTraceType.Unknown });
73
+ return this.classifyCdsAndReturn(call, { id: call.id, types: [InputType.Random], trace: InputTraceType.Unknown });
74
+ }
75
+ else if (matchesList(call, this.config.systemFns)) {
76
+ return this.classifyCdsAndReturn(call, { id: call.id, types: [InputType.System], trace: InputTraceType.Unknown });
77
+ }
78
+ else if (matchesList(call, this.config.ffiFns)) {
79
+ return this.classifyCdsAndReturn(call, { id: call.id, types: [InputType.Ffi], trace: InputTraceType.Unknown });
80
+ }
81
+ else if (matchesList(call, this.config.langFns)) {
82
+ return this.classifyCdsAndReturn(call, { id: call.id, types: [InputType.Lang], trace: InputTraceType.Unknown });
83
+ }
84
+ else if (matchesList(call, this.config.optionsFns)) {
85
+ return this.classifyCdsAndReturn(call, { id: call.id, types: [InputType.Options], trace: InputTraceType.Unknown });
69
86
  }
70
87
  else {
71
88
  // if it is not pure, we cannot classify based on the inputs, in that case we do not know!
72
- return this.classifyCdsAndReturn(call, { id: call.id, type: [InputType.Unknown], trace: InputTraceType.Unknown });
89
+ return this.classifyCdsAndReturn(call, { id: call.id, types: [InputType.Unknown], trace: InputTraceType.Unknown });
73
90
  }
74
91
  }
75
92
  // Otherwise, classify by arguments; pure functions get Known/Pure handling
@@ -91,7 +108,7 @@ class InputClassifier {
91
108
  }
92
109
  const classified = this.classifyEntry(argVtx);
93
110
  // collect all observed types from this argument
94
- argTypes.push(...classified.type);
111
+ argTypes.push(...classified.types);
95
112
  if (classified.cds) {
96
113
  cdTypes.push(...classified.cds);
97
114
  }
@@ -100,15 +117,15 @@ class InputClassifier {
100
117
  // all arguments only contain constant-like types -> derived constant
101
118
  const allConstLike = argTypes.length > 0 && argTypes.every(t => t === InputType.Constant || t === InputType.DerivedConstant);
102
119
  if (allConstLike) {
103
- return this.classifyCdsAndReturn(call, (0, objects_1.compactRecord)({ id: call.id, type: [InputType.DerivedConstant], trace: InputTraceType.Pure, cds }));
120
+ return this.classifyCdsAndReturn(call, (0, objects_1.compactRecord)({ id: call.id, types: [InputType.DerivedConstant], trace: InputTraceType.Pure, cds }));
104
121
  }
105
122
  argTypes.push(InputType.DerivedConstant);
106
- return this.classifyCdsAndReturn(call, (0, objects_1.compactRecord)({ id: call.id, type: (0, arrays_1.uniqueArray)(argTypes), trace: InputTraceType.Known, cds }));
123
+ return this.classifyCdsAndReturn(call, (0, objects_1.compactRecord)({ id: call.id, types: (0, arrays_1.uniqueArray)(argTypes), trace: InputTraceType.Known, cds }));
107
124
  }
108
125
  classifyVariable(vtx) {
109
126
  const origins = df_helper_1.Dataflow.origin(this.dfg, vtx.id);
110
127
  if (origins === undefined) {
111
- return this.classifyCdsAndReturn(vtx, { id: vtx.id, type: [InputType.Unknown], trace: InputTraceType.Unknown });
128
+ return this.classifyCdsAndReturn(vtx, { id: vtx.id, types: this.isDefinedByOnCall(vtx.id) ? [InputType.Scope] : [InputType.Unknown], trace: InputTraceType.Unknown });
112
129
  }
113
130
  const types = [];
114
131
  const cds = [];
@@ -121,13 +138,19 @@ class InputClassifier {
121
138
  if (o.type === 0 /* OriginType.ReadVariableOrigin */ || o.type === 1 /* OriginType.WriteVariableOrigin */) {
122
139
  const v = this.dfg.getVertex(o.id);
123
140
  if (v) {
141
+ // if the referenced definition is linked via defined-by-on-call to another
142
+ // id (e.g., a parameter linked to a caller argument), mark it as a Scope origin
143
+ if (this.isDefinedByOnCall(v.id)) {
144
+ types.push(InputType.Scope);
145
+ allPure = false;
146
+ }
124
147
  // if this is a variable definition that is a parameter, classify as Parameter
125
148
  if (v.tag === vertex_1.VertexType.VariableDefinition && this.dfg.idMap?.get(v.id)?.info.role === "param-n" /* RoleInParent.ParameterName */) {
126
149
  types.push(InputType.Parameter);
127
150
  continue;
128
151
  }
129
152
  const c = this.classifyEntry(v);
130
- types.push(...c.type);
153
+ types.push(...c.types);
131
154
  if (c.cds) {
132
155
  cds.push(...c.cds);
133
156
  }
@@ -144,7 +167,7 @@ class InputClassifier {
144
167
  const v = this.dfg.getVertex(o.id);
145
168
  if (v) {
146
169
  const c = this.classifyEntry(v);
147
- types.push(...c.type);
170
+ types.push(...c.types);
148
171
  if (c.cds) {
149
172
  cds.push(...c.cds);
150
173
  }
@@ -162,17 +185,17 @@ class InputClassifier {
162
185
  }
163
186
  const t = types.length === 0 ? [InputType.Unknown] : (0, arrays_1.uniqueArray)(types);
164
187
  const trace = allPure ? InputTraceType.Pure : InputTraceType.Alias;
165
- return this.classifyCdsAndReturn(vtx, { id: vtx.id, type: t, trace, cds: cds.length === 0 ? undefined : (0, arrays_1.uniqueArray)(cds) });
188
+ return this.classifyCdsAndReturn(vtx, { id: vtx.id, types: t, trace, cds: cds.length === 0 ? undefined : (0, arrays_1.uniqueArray)(cds) });
166
189
  }
167
190
  classifyVariableDefinition(vtx) {
168
191
  // parameter definitions are classified as Parameter
169
192
  if (this.dfg.idMap?.get(vtx.id)?.info.role === "param-n" /* RoleInParent.ParameterName */) {
170
- return this.classifyCdsAndReturn(vtx, { id: vtx.id, type: [InputType.Parameter], trace: InputTraceType.Unknown });
193
+ return this.classifyCdsAndReturn(vtx, { id: vtx.id, types: [InputType.Parameter], trace: InputTraceType.Unknown });
171
194
  }
172
195
  const sources = vtx.source;
173
196
  if (sources === undefined || sources.length === 0) {
174
197
  // fallback to unknown if we cannot find the value
175
- return this.classifyCdsAndReturn(vtx, { id: vtx.id, type: [InputType.Unknown], trace: InputTraceType.Unknown });
198
+ return this.classifyCdsAndReturn(vtx, { id: vtx.id, types: [InputType.Unknown], trace: InputTraceType.Unknown });
176
199
  }
177
200
  const types = [];
178
201
  const cds = [];
@@ -181,7 +204,7 @@ class InputClassifier {
181
204
  const tv = this.dfg.getVertex(tid);
182
205
  if (tv) {
183
206
  const c = this.classifyEntry(tv);
184
- types.push(...c.type);
207
+ types.push(...c.types);
185
208
  if (c.cds) {
186
209
  cds.push(...c.cds);
187
210
  }
@@ -195,7 +218,7 @@ class InputClassifier {
195
218
  }
196
219
  const t = types.length === 0 ? [InputType.Unknown] : (0, arrays_1.uniqueArray)(types);
197
220
  const trace = allPure ? InputTraceType.Pure : InputTraceType.Alias;
198
- return this.classifyCdsAndReturn(vtx, { id: vtx.id, type: t, trace, cds: cds.length === 0 ? undefined : (0, arrays_1.uniqueArray)(cds) });
221
+ return this.classifyCdsAndReturn(vtx, { id: vtx.id, types: t, trace, cds: cds.length === 0 ? undefined : (0, arrays_1.uniqueArray)(cds) });
199
222
  }
200
223
  classifyCdsAndReturn(vtx, src) {
201
224
  if (vtx.cds) {
@@ -205,7 +228,7 @@ class InputClassifier {
205
228
  return undefined;
206
229
  }
207
230
  const e = this.classifyEntry(cv);
208
- return e.cds ? [...e.type, ...e.cds] : [...e.type];
231
+ return e.cds ? [...e.types, ...e.cds] : [...e.types];
209
232
  }).filter(assert_1.isNotUndefined).concat(src.cds ?? []));
210
233
  if (cds.length > 0) {
211
234
  src.cds = cds;
@@ -224,13 +247,13 @@ class InputClassifier {
224
247
  * joining differing lattice elements.
225
248
  *
226
249
  *```
227
- * [ Unknown ]
228
- * / / | \ \
229
- *[Param] [File] [Net] [Rand] [Scope]
230
- * \ \ | / /
231
- * [ DerivedConstant ]
232
- * |
233
- * [ Constant ]
250
+ * [ Unknown ]
251
+ * |
252
+ * [Param] [File] [Net], ...
253
+ * |
254
+ * [ DerivedConstant ]
255
+ * |
256
+ * [ Constant ]
234
257
  *```
235
258
  *
236
259
  */
@@ -240,6 +263,14 @@ var InputType;
240
263
  InputType["File"] = "file";
241
264
  InputType["Network"] = "net";
242
265
  InputType["Random"] = "rand";
266
+ /** Calls to system/system2 and similar */
267
+ InputType["System"] = "system";
268
+ /** Calls to .C / Fortran interfaces */
269
+ InputType["Ffi"] = "ffi";
270
+ /** Language objects (quote/substitute/etc.) */
271
+ InputType["Lang"] = "lang";
272
+ /** Global options / option accessors (options, getOption) */
273
+ InputType["Options"] = "options";
243
274
  InputType["Constant"] = "const";
244
275
  /** Read from environment/call scope */
245
276
  InputType["Scope"] = "scope";
package/util/version.js CHANGED
@@ -6,7 +6,7 @@ exports.printVersionInformation = printVersionInformation;
6
6
  const semver_1 = require("semver");
7
7
  const assert_1 = require("./assert");
8
8
  // this is automatically replaced with the current version by release-it
9
- const version = '2.10.3';
9
+ const version = '2.10.4';
10
10
  /**
11
11
  * Retrieves the current flowR version as a new {@link SemVer} object.
12
12
  */
@@ -1,83 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PROBLEMATIC_EVAL = void 0;
4
- const linter_format_1 = require("../linter-format");
5
- const flowr_search_builder_1 = require("../../search/flowr-search-builder");
6
- const range_1 = require("../../util/range");
7
- const linter_tags_1 = require("../linter-tags");
8
- const simple_input_classifier_1 = require("../../queries/catalog/input-sources-query/simple-input-classifier");
9
- const query_1 = require("../../queries/query");
10
- const parse_1 = require("../../slicing/criterion/parse");
11
- /**
12
- * Format a list of input sources either as a single-line string (inline) or a block.
13
- * - inline: returns a semicolon-separated single-line summary
14
- * - block: returns an array of lines (to be joined with newlines by the caller)
15
- */
16
- function formatInputSources(inputs, inline = true) {
17
- if (!inputs || inputs.length === 0) {
18
- return inline ? '' : [];
19
- }
20
- if (inline) {
21
- return inputs.map(s => `${s.id} (type: ${Array.isArray(s.type) ? '[' + s.type.join(',') + ']' : s.type}, trace: ${s.trace}${s.cds ? ', cds: [' + s.cds.join(',') + ']' : ''})`).join('; ');
22
- }
23
- return inputs.map(s => `- ${s.id}: type=${Array.isArray(s.type) ? '[' + s.type.join(',') + ']' : s.type}, trace=${s.trace}${s.cds ? ', cds=[' + s.cds.join(',') + ']' : ''}`);
24
- }
25
- exports.PROBLEMATIC_EVAL = {
26
- /* create a search that finds calls that look like eval-like functions */
27
- createSearch: config => flowr_search_builder_1.Q.fromQuery({
28
- type: 'call-context',
29
- callName: config.considerAsEval,
30
- callNameExact: false
31
- }),
32
- processSearchResult: async (elements, _config, data) => {
33
- const results = [];
34
- for (const element of elements.getElements()) {
35
- const nid = element.node.info.id;
36
- // run an input-sources query for this eval-like call
37
- const criterion = parse_1.SlicingCriterion.fromId(nid);
38
- const q = { type: 'input-sources', criterion };
39
- const all = await (0, query_1.executeQueries)({ analyzer: data.analyzer }, [q]);
40
- const inputSourcesResult = all['input-sources'];
41
- const sources = inputSourcesResult?.results?.[criterion] ?? [];
42
- // if any input is not a constant or derived constant, flag it
43
- const problematic = sources.some(s => Array.isArray(s.type)
44
- ? s.type.some(t => t !== simple_input_classifier_1.InputType.Constant && t !== simple_input_classifier_1.InputType.DerivedConstant)
45
- : (s.type !== simple_input_classifier_1.InputType.Constant && s.type !== simple_input_classifier_1.InputType.DerivedConstant));
46
- if (problematic) {
47
- results.push({
48
- involvedId: nid,
49
- certainty: sources.some(s => Array.isArray(s.type) ? s.type.includes(simple_input_classifier_1.InputType.Unknown) : s.type === simple_input_classifier_1.InputType.Unknown) ? linter_format_1.LintingResultCertainty.Uncertain : linter_format_1.LintingResultCertainty.Certain,
50
- loc: range_1.SourceLocation.fromNode(element.node) ?? range_1.SourceLocation.invalid(),
51
- sources
52
- });
53
- }
54
- }
55
- return {
56
- results,
57
- '.meta': {}
58
- };
59
- },
60
- /* helper to format input sources for pretty printing */
61
- prettyPrint: {
62
- [linter_format_1.LintingPrettyPrintContext.Query]: result => {
63
- const inputs = result.sources ?? [];
64
- const srcStr = formatInputSources(inputs, true);
65
- return `Use of eval-like function at ${range_1.SourceLocation.format(result.loc)}${srcStr ? `; inputs: ${srcStr}` : ''}`;
66
- },
67
- [linter_format_1.LintingPrettyPrintContext.Full]: result => {
68
- const inputs = result.sources ?? [];
69
- const srcLines = formatInputSources(inputs, false);
70
- return `Use of eval-like function at ${range_1.SourceLocation.format(result.loc)} is potentially problematic${srcLines.length ? '\nInputs:\n' + srcLines.join('\n') : ''}`;
71
- }
72
- },
73
- info: {
74
- name: 'Problematic eval',
75
- description: 'Detects uses of eval-like functions whose inputs are not statically constant. Prints the computed input-sources for the eval and flags usages that depend on non-constant/trusted inputs.',
76
- tags: [linter_tags_1.LintingRuleTag.Security, linter_tags_1.LintingRuleTag.Smell, linter_tags_1.LintingRuleTag.Readability, linter_tags_1.LintingRuleTag.Performance],
77
- certainty: linter_format_1.LintingRuleCertainty.BestEffort,
78
- defaultConfig: {
79
- considerAsEval: '^eval$'
80
- }
81
- }
82
- };
83
- //# sourceMappingURL=problematic-eval.js.map