Compare commits

...

21 Commits

Author SHA1 Message Date
Mariusz Kliber
09c7ef4639 Debug: removed grep 2022-11-30 17:57:31 +00:00
Mariusz Kliber
6357539da5 Debug: testing API calls 2022-11-30 17:57:31 +00:00
Mariusz Kliber
05a899ea53 Debug: dump user scope 2022-11-30 17:57:31 +00:00
Mariusz Kliber
796763d027 Apply suggestions from aibaars
Co-authored-by: Arthur Baars <aibaars@github.com>
2022-11-30 17:57:31 +00:00
Mariusz Kliber
d9bff8a0a3 CI: Add Internal CI Checks workflow 2022-11-30 17:57:31 +00:00
Tony Torralba
69b112ccbe Fix expectations in data.swift 2022-11-30 17:57:31 +00:00
Karim Ali
89d47d90d6 update the expected output for CWE-079
Now that we have support for taint through fields of String, we can now detect certain flows that we previously marked as [NOT DETECTED]. This commit updates the expected output of CWE-079 (and the in-code annotation of the accompanying test case) to reflect that update.
2022-11-30 17:57:31 +00:00
Karim Ali
f57f26cdfd fix expected output for LocalTaint 2022-11-30 17:57:31 +00:00
Karim Ali
5cc94e0ac5 fix expected output for TaintInline 2022-11-30 17:57:31 +00:00
Karim Ali
1a1c1a4e26 fix expected output for Taint.ql 2022-11-30 17:57:31 +00:00
Karim Ali
5782bfdf0a updated expected output for LocalTaint and Tain 2022-11-30 17:57:31 +00:00
Karim Ali
b9153fb8a0 add test case for unicodeScalars 2022-11-30 17:57:31 +00:00
Karim Ali
1394ae3ac2 add test case for utf8CString 2022-11-30 17:57:30 +00:00
Karim Ali
1296dd0a57 add taint steps for fields of String
if a String is tainted, then all its fields (including those declared in extensions) should be tainted as well
2022-11-30 17:57:30 +00:00
Paolo Tranquilli
8d2f84ad2a Swift: add --force to codegen 2022-11-30 17:57:30 +00:00
Paolo Tranquilli
da7d93bf3d Swift: make codegen run when no registry is there 2022-11-30 17:57:30 +00:00
Paolo Tranquilli
e4b3140be8 Swift: make codegen resilient to formatting errors
More in general, the managed renderer flow does things more sensibly
in case an exception is thrown:
* it will not remove any file
* it will drop already written files from the registry, so that codegen
  won't be skipped for those files during the next run
2022-11-30 17:57:30 +00:00
Owen Mansel-Chan
c8c5fc7b00 Remove @codeql-go from code owners for dataflow 2022-11-30 17:57:30 +00:00
Owen Mansel-Chan
91050348db Use ArgumentPosition instead of int
This matches what all of the other languages do.
2022-11-30 17:57:30 +00:00
Owen Mansel-Chan
cfc5595e84 Dataflow: Sync. 2022-11-30 17:57:30 +00:00
Owen Mansel-Chan
b124fe56d9 Fix variable name (source should be sink) 2022-11-30 17:57:30 +00:00
54 changed files with 356 additions and 73 deletions

View File

@@ -0,0 +1,30 @@
name: Internal CI Checks
# This workflows checks if the author of a PR is a member of the CodeQL team
# and adds `ready-for-internal-ci` label to trigger the internal CI
on:
pull_request_target:
jobs:
set-label:
runs-on: ubuntu-latest
steps:
- name: Set a label to trigger internal CI checks
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
USERNAME: ${{ github.event.pull_request.user.login }}
PR_NUMBER: ${{ github.event.pull_request.number }}
LABEL: "ready-for-internal-ci"
run: |
set +eo pipefail
echo "Testing API calls"
gh api -i /repos/github/codeql
echo "Checking if user $USERNAME is a member of the CodeQL team"
if [ "$(gh api "/orgs/github/teams/codeql/memberships/$USERNAME" --jq .state)" == "active" ]; then
echo "User $USERNAME is a member of the CodeQL team"
echo "Adding '${LABEL}' label"
gh pr edit "${PR_NUMBER}" --repo "$GITHUB_REPOSITORY" --add-label "$LABEL"
else
echo "User $USERNAME is not a member of the CodeQL team"
echo "To trigger the internal CI, a maintainer needs to add the '${LABEL}' label"
fi

View File

@@ -12,13 +12,6 @@
# ML-powered queries
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
# Notify members of codeql-go about PRs to the shared data-flow library files
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @github/codeql-java @github/codeql-go
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @github/codeql-java @github/codeql-go
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @github/codeql-java @github/codeql-go
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
# CodeQL tools and associated docs
/docs/codeql/codeql-cli/ @github/codeql-cli-reviewers
/docs/codeql/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -17,7 +17,7 @@ class SummarizedCallableBase = FlowSummary::SummarizedCallableBase;
DataFlowCallable inject(SummarizedCallable c) { result.asSummarizedCallable() = c }
/** Gets the parameter position of the instance parameter. */
int instanceParameterPosition() { result = -1 }
ArgumentPosition instanceParameterPosition() { result = -1 }
/** Gets the synthesized summary data-flow node for the given values. */
Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = getSummaryNode(c, state) }

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -42,6 +42,8 @@ def _parse_args() -> argparse.Namespace:
help="output directory for generated C++ files, required if trap or cpp is provided to --generate")
p.add_argument("--generated-registry", type=_abspath, default=paths.swift_dir / "ql/.generated.list",
help="registry file containing information about checked-in generated code")
p.add_argument("--force", "-f", action="store_true",
help="generate all files without skipping unchanged files and overwriting modified ones")
return p.parse_args()

View File

@@ -302,7 +302,8 @@ def generate(opts, renderer):
imports = {}
with renderer.manage(generated=generated, stubs=stubs, registry=opts.generated_registry) as renderer:
with renderer.manage(generated=generated, stubs=stubs, registry=opts.generated_registry,
force=opts.force) as renderer:
db_classes = [cls for cls in classes.values() if not cls.ipa]
renderer.render(ql.DbClasses(db_classes), out / "Raw.qll")

View File

@@ -59,8 +59,8 @@ class Renderer:
log.debug(f"{mnemonic}: generated {output.name}")
def manage(self, generated: typing.Iterable[pathlib.Path], stubs: typing.Iterable[pathlib.Path],
registry: pathlib.Path) -> "RenderManager":
return RenderManager(self._swift_dir, generated, stubs, registry)
registry: pathlib.Path, force: bool = False) -> "RenderManager":
return RenderManager(self._swift_dir, generated, stubs, registry, force)
class RenderManager(Renderer):
@@ -87,9 +87,10 @@ class RenderManager(Renderer):
def __init__(self, swift_dir: pathlib.Path, generated: typing.Iterable[pathlib.Path],
stubs: typing.Iterable[pathlib.Path],
registry: pathlib.Path):
registry: pathlib.Path, force: bool = False):
super().__init__(swift_dir)
self._registry_path = registry
self._force = force
self._hashes = {}
self.written = set()
self._existing = set()
@@ -103,12 +104,18 @@ class RenderManager(Renderer):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
for f in self._existing - self._skipped - self.written:
self._hashes.pop(self._get_path(f), None)
f.unlink(missing_ok=True)
log.info(f"removed {f.name}")
for f in self.written:
self._hashes[self._get_path(f)].post = self._hash_file(f)
if exc_val is None:
for f in self._existing - self._skipped - self.written:
self._hashes.pop(self._get_path(f), None)
f.unlink(missing_ok=True)
log.info(f"removed {f.name}")
for f in self.written:
self._hashes[self._get_path(f)].post = self._hash_file(f)
else:
# if an error was encountered, drop already written files from the registry
# so that they get the chance to be regenerated again during the next run
for f in self.written:
self._hashes.pop(self._get_path(f), None)
self._dump_registry()
def _do_write(self, mnemonic: str, contents: str, output: pathlib.Path):
@@ -126,10 +133,13 @@ class RenderManager(Renderer):
for f in generated:
self._existing.add(f)
rel_path = self._get_path(f)
if rel_path not in self._hashes:
if self._force:
pass
elif rel_path not in self._hashes:
log.warning(f"{rel_path} marked as generated but absent from the registry")
elif self._hashes[rel_path].post != self._hash_file(f):
raise Error(f"{rel_path} is generated but was modified, please revert the file")
raise Error(f"{rel_path} is generated but was modified, please revert the file "
"or pass --force to overwrite")
def _process_stubs(self, stubs: typing.Iterable[pathlib.Path]):
for f in stubs:
@@ -138,10 +148,13 @@ class RenderManager(Renderer):
self._hashes.pop(rel_path, None)
continue
self._existing.add(f)
if rel_path not in self._hashes:
if self._force:
pass
elif rel_path not in self._hashes:
log.warning(f"{rel_path} marked as stub but absent from the registry")
elif self._hashes[rel_path].post != self._hash_file(f):
raise Error(f"{rel_path} is a stub marked as generated, but it was modified")
raise Error(f"{rel_path} is a stub marked as generated, but it was modified, "
"please remove the `// generated` header, revert the file or pass --force to overwrite it")
@staticmethod
def is_customized_stub(file: pathlib.Path) -> bool:
@@ -165,12 +178,18 @@ class RenderManager(Renderer):
return h.hexdigest()
def _load_registry(self):
with open(self._registry_path) as reg:
for line in reg:
filename, prehash, posthash = line.split()
self._hashes[pathlib.Path(filename)] = self.Hashes(prehash, posthash)
if self._force:
return
try:
with open(self._registry_path) as reg:
for line in reg:
filename, prehash, posthash = line.split()
self._hashes[pathlib.Path(filename)] = self.Hashes(prehash, posthash)
except FileNotFoundError:
pass
def _dump_registry(self):
self._registry_path.parent.mkdir(parents=True, exist_ok=True)
with open(self._registry_path, 'w') as out:
for f, hashes in sorted(self._hashes.items()):
print(f, hashes.pre, hashes.post, file=out)

View File

@@ -48,6 +48,7 @@ def qlgen_opts(opts):
opts.generated_registry = generated_registry_path()
opts.ql_format = True
opts.swift_dir = paths.swift_dir
opts.force = False
return opts
@@ -430,7 +431,9 @@ def test_format_error(opts, generate, render_manager, run_mock):
generate([schema.Class('A')])
def test_manage_parameters(opts, generate, renderer):
@pytest.mark.parametrize("force", [False, True])
def test_manage_parameters(opts, generate, renderer, force):
opts.force = force
ql_a = opts.ql_output / "A.qll"
ql_b = opts.ql_output / "B.qll"
stub_a = opts.ql_stub_output / "A.qll"
@@ -448,7 +451,7 @@ def test_manage_parameters(opts, generate, renderer):
generate([schema.Class('A')])
assert renderer.mock_calls == [
mock.call.manage(generated={ql_a, ql_b, test_a, test_b, import_file()}, stubs={stub_a, stub_b},
registry=opts.generated_registry)
registry=opts.generated_registry, force=force)
]

View File

@@ -76,6 +76,24 @@ def test_managed_render(pystache_renderer, sut):
]
def test_managed_render_with_no_registry(pystache_renderer, sut):
data = mock.Mock(spec=("template",))
text = "some text"
pystache_renderer.render_name.side_effect = (text,)
output = paths.swift_dir / "some/output.txt"
registry = paths.swift_dir / "a/registry.list"
with sut.manage(generated=(), stubs=(), registry=registry) as renderer:
renderer.render(data, output)
assert renderer.written == {output}
assert_file(output, text)
assert_file(registry, f"some/output.txt {hash(text)} {hash(text)}\n")
assert pystache_renderer.mock_calls == [
mock.call.render_name(data.template, data, generator=paths.exe_file.relative_to(paths.swift_dir)),
]
def test_managed_render_with_post_processing(pystache_renderer, sut):
data = mock.Mock(spec=("template",))
text = "some text"
@@ -192,6 +210,45 @@ def test_managed_render_with_modified_stub_file_not_marked_as_generated(pystache
assert_file(registry, "")
class MyError(Exception):
pass
def test_managed_render_exception_drops_written_from_registry(pystache_renderer, sut):
data = mock.Mock(spec=("template",))
text = "some text"
pystache_renderer.render_name.side_effect = (text,)
output = paths.swift_dir / "some/output.txt"
registry = paths.swift_dir / "a/registry.list"
write(output, text)
write(registry, "a a a\n"
f"some/output.txt whatever {hash(text)}\n"
"b b b")
with pytest.raises(MyError):
with sut.manage(generated=(), stubs=(), registry=registry) as renderer:
renderer.render(data, output)
raise MyError
assert_file(registry, "a a a\nb b b\n")
def test_managed_render_exception_does_not_erase(pystache_renderer, sut):
output = paths.swift_dir / "some/output.txt"
stub = paths.swift_dir / "some/stub.txt"
registry = paths.swift_dir / "a/registry.list"
write(output)
write(stub, "// generated bla bla")
write(registry)
with pytest.raises(MyError):
with sut.manage(generated=(output,), stubs=(stub,), registry=registry) as renderer:
raise MyError
assert output.is_file()
assert stub.is_file()
def test_render_with_extensions(pystache_renderer, sut):
data = mock.Mock(spec=("template", "extensions"))
data.template = "test_template"
@@ -210,5 +267,70 @@ def test_render_with_extensions(pystache_renderer, sut):
assert_file(expected_output, expected_contents)
def test_managed_render_with_force_not_skipping_generated_file(pystache_renderer, sut):
data = mock.Mock(spec=("template",))
output = paths.swift_dir / "some/output.txt"
some_output = "some output"
registry = paths.swift_dir / "a/registry.list"
write(output, some_output)
write(registry, f"some/output.txt {hash(some_output)} {hash(some_output)}\n")
pystache_renderer.render_name.side_effect = (some_output,)
with sut.manage(generated=(output,), stubs=(), registry=registry, force=True) as renderer:
renderer.render(data, output)
assert renderer.written == {output}
assert_file(output, some_output)
assert_file(registry, f"some/output.txt {hash(some_output)} {hash(some_output)}\n")
assert pystache_renderer.mock_calls == [
mock.call.render_name(data.template, data, generator=paths.exe_file.relative_to(paths.swift_dir)),
]
def test_managed_render_with_force_not_skipping_stub_file(pystache_renderer, sut):
data = mock.Mock(spec=("template",))
stub = paths.swift_dir / "some/stub.txt"
some_output = "// generated some output"
some_processed_output = "// generated some processed output"
registry = paths.swift_dir / "a/registry.list"
write(stub, some_processed_output)
write(registry, f"some/stub.txt {hash(some_output)} {hash(some_processed_output)}\n")
pystache_renderer.render_name.side_effect = (some_output,)
with sut.manage(generated=(), stubs=(stub,), registry=registry, force=True) as renderer:
renderer.render(data, stub)
assert renderer.written == {stub}
assert_file(stub, some_output)
assert_file(registry, f"some/stub.txt {hash(some_output)} {hash(some_output)}\n")
assert pystache_renderer.mock_calls == [
mock.call.render_name(data.template, data, generator=paths.exe_file.relative_to(paths.swift_dir)),
]
def test_managed_render_with_force_ignores_modified_generated_file(sut):
output = paths.swift_dir / "some/output.txt"
some_processed_output = "// some processed output"
registry = paths.swift_dir / "a/registry.list"
write(output, "// something else")
write(registry, f"some/output.txt whatever {hash(some_processed_output)}\n")
with sut.manage(generated=(output,), stubs=(), registry=registry, force=True):
pass
def test_managed_render_with_force_ignores_modified_stub_file_still_marked_as_generated(sut):
stub = paths.swift_dir / "some/stub.txt"
some_processed_output = "// generated some processed output"
registry = paths.swift_dir / "a/registry.list"
write(stub, "// generated something else")
write(registry, f"some/stub.txt whatever {hash(some_processed_output)}\n")
with sut.manage(generated=(), stubs=(stub,), registry=registry, force=True):
pass
if __name__ == '__main__':
sys.exit(pytest.main([__file__] + sys.argv[1:]))

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes

View File

@@ -1,5 +1,7 @@
import swift
private import codeql.swift.dataflow.DataFlow
private import codeql.swift.dataflow.ExternalFlow
private import codeql.swift.dataflow.FlowSteps
private class StringSource extends SourceModelCsv {
override predicate row(string row) {
@@ -16,3 +18,15 @@ private class StringSource extends SourceModelCsv {
]
}
}
/**
* A content implying that, if a `String` is tainted, then all its fields are tainted.
*/
private class StringFieldsInheritTaint extends TaintInheritingContent,
DataFlow::Content::FieldContent {
StringFieldsInheritTaint() {
this.getField().getEnclosingDecl().(ClassOrStructDecl).getFullName() = "String" or
this.getField().getEnclosingDecl().(ExtensionDecl).getExtendedTypeDecl().getFullName() =
"String"
}
}

View File

@@ -120,11 +120,13 @@
| data.swift:80:6:80:6 | SSA def(dataClean) | data.swift:84:12:84:12 | dataClean |
| data.swift:80:18:80:18 | Data.Type | data.swift:80:18:80:18 | call to init(_:) |
| data.swift:80:18:80:36 | call to init(_:) | data.swift:80:6:80:6 | SSA def(dataClean) |
| data.swift:80:23:80:23 | 123456 | data.swift:80:23:80:32 | .utf8 |
| data.swift:80:23:80:32 | .utf8 | data.swift:80:18:80:36 | call to init(_:) |
| data.swift:81:6:81:6 | SSA def(dataTainted) | data.swift:82:26:82:26 | dataTainted |
| data.swift:81:20:81:20 | Data.Type | data.swift:81:20:81:20 | call to init(_:) |
| data.swift:81:20:81:51 | call to init(_:) | data.swift:81:6:81:6 | SSA def(dataTainted) |
| data.swift:81:25:81:47 | .utf8 | data.swift:81:20:81:51 | call to init(_:) |
| data.swift:81:26:81:33 | call to source() | data.swift:81:25:81:47 | .utf8 |
| data.swift:82:6:82:6 | SSA def(dataTainted2) | data.swift:86:12:86:12 | dataTainted2 |
| data.swift:82:21:82:21 | Data.Type | data.swift:82:21:82:21 | call to init(_:) |
| data.swift:82:21:82:37 | call to init(_:) | data.swift:82:6:82:6 | SSA def(dataTainted2) |
@@ -898,9 +900,13 @@
| string.swift:81:31:81:31 | clean | string.swift:84:13:84:13 | clean |
| string.swift:82:31:82:31 | tainted | string.swift:85:13:85:13 | tainted |
| string.swift:84:13:84:13 | [post] clean | string.swift:87:13:87:13 | clean |
| string.swift:84:13:84:13 | clean | string.swift:84:13:84:19 | .description |
| string.swift:84:13:84:13 | clean | string.swift:87:13:87:13 | clean |
| string.swift:85:13:85:13 | [post] tainted | string.swift:88:13:88:13 | tainted |
| string.swift:85:13:85:13 | tainted | string.swift:85:13:85:21 | .description |
| string.swift:85:13:85:13 | tainted | string.swift:88:13:88:13 | tainted |
| string.swift:87:13:87:13 | clean | string.swift:87:13:87:19 | .debugDescription |
| string.swift:88:13:88:13 | tainted | string.swift:88:13:88:21 | .debugDescription |
| string.swift:91:7:91:7 | SSA def(self) | string.swift:91:7:91:7 | self[return] |
| string.swift:91:7:91:7 | self | string.swift:91:7:91:7 | SSA def(self) |
| string.swift:93:5:93:5 | SSA def(self) | string.swift:93:5:93:29 | self[return] |
@@ -923,6 +929,17 @@
| string.swift:109:23:109:77 | call to init(data:encoding:) | string.swift:109:7:109:7 | SSA def(stringTainted) |
| string.swift:111:12:111:12 | stringClean | string.swift:111:12:111:23 | ...! |
| string.swift:112:12:112:12 | stringTainted | string.swift:112:12:112:25 | ...! |
| string.swift:120:7:120:7 | SSA def(clean) | string.swift:125:13:125:13 | clean |
| string.swift:120:15:120:15 | | string.swift:120:7:120:7 | SSA def(clean) |
| string.swift:121:7:121:7 | SSA def(tainted) | string.swift:126:13:126:13 | tainted |
| string.swift:121:17:121:25 | call to source2() | string.swift:121:17:121:27 | .utf8 |
| string.swift:121:17:121:27 | .utf8 | string.swift:121:7:121:7 | SSA def(tainted) |
| string.swift:122:7:122:7 | SSA def(taintedCString) | string.swift:127:13:127:13 | taintedCString |
| string.swift:122:24:122:32 | call to source2() | string.swift:122:24:122:34 | .utf8CString |
| string.swift:122:24:122:34 | .utf8CString | string.swift:122:7:122:7 | SSA def(taintedCString) |
| string.swift:123:7:123:7 | SSA def(taintedUnicodeScalars) | string.swift:128:13:128:13 | taintedUnicodeScalars |
| string.swift:123:31:123:39 | call to source2() | string.swift:123:31:123:41 | .unicodeScalars |
| string.swift:123:31:123:41 | .unicodeScalars | string.swift:123:7:123:7 | SSA def(taintedUnicodeScalars) |
| subscript.swift:1:7:1:7 | SSA def(self) | subscript.swift:1:7:1:7 | self[return] |
| subscript.swift:1:7:1:7 | SSA def(self) | subscript.swift:1:7:1:7 | self[return] |
| subscript.swift:1:7:1:7 | self | subscript.swift:1:7:1:7 | SSA def(self) |

View File

@@ -1,4 +1,5 @@
edges
| data.swift:24:5:24:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : |
| data.swift:25:2:25:66 | [summary param] 0 in init(base64Encoded:options:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(base64Encoded:options:) : |
| data.swift:26:2:26:61 | [summary param] 0 in init(buffer:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(buffer:) : |
| data.swift:27:2:27:62 | [summary param] 0 in init(buffer:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(buffer:) : |
@@ -34,6 +35,14 @@ edges
| data.swift:62:2:62:58 | [summary param] this in shuffled(using:) : | file://:0:0:0:0 | [summary] to write: return (return) in shuffled(using:) : |
| data.swift:63:2:63:123 | [summary param] this in trimmingPrefix(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in trimmingPrefix(_:) : |
| data.swift:64:2:64:72 | [summary param] this in trimmingPrefix(while:) : | file://:0:0:0:0 | [summary] to write: return (return) in trimmingPrefix(while:) : |
| data.swift:81:20:81:51 | call to init(_:) : | data.swift:82:26:82:26 | dataTainted : |
| data.swift:81:20:81:51 | call to init(_:) : | data.swift:85:12:85:12 | dataTainted |
| data.swift:81:25:81:47 | .utf8 : | data.swift:24:5:24:29 | [summary param] 0 in init(_:) : |
| data.swift:81:25:81:47 | .utf8 : | data.swift:81:20:81:51 | call to init(_:) : |
| data.swift:81:26:81:33 | call to source() : | data.swift:81:25:81:47 | .utf8 : |
| data.swift:82:21:82:37 | call to init(_:) : | data.swift:86:12:86:12 | dataTainted2 |
| data.swift:82:26:82:26 | dataTainted : | data.swift:24:5:24:29 | [summary param] 0 in init(_:) : |
| data.swift:82:26:82:26 | dataTainted : | data.swift:82:21:82:37 | call to init(_:) : |
| data.swift:89:21:89:71 | call to init(base64Encoded:options:) : | data.swift:90:12:90:12 | dataTainted3 |
| data.swift:89:41:89:48 | call to source() : | data.swift:25:2:25:66 | [summary param] 0 in init(base64Encoded:options:) : |
| data.swift:89:41:89:48 | call to source() : | data.swift:89:21:89:71 | call to init(base64Encoded:options:) : |
@@ -301,6 +310,11 @@ edges
| string.swift:28:17:28:25 | call to source2() : | string.swift:35:13:35:23 | ... .+(_:_:) ... |
| string.swift:28:17:28:25 | call to source2() : | string.swift:36:13:36:23 | ... .+(_:_:) ... |
| string.swift:28:17:28:25 | call to source2() : | string.swift:39:13:39:29 | ... .+(_:_:) ... |
| string.swift:74:17:74:25 | call to source2() : | string.swift:85:13:85:21 | .description |
| string.swift:74:17:74:25 | call to source2() : | string.swift:88:13:88:21 | .debugDescription |
| string.swift:121:17:121:25 | call to source2() : | string.swift:126:13:126:13 | tainted |
| string.swift:122:24:122:32 | call to source2() : | string.swift:127:13:127:13 | taintedCString |
| string.swift:123:31:123:39 | call to source2() : | string.swift:128:13:128:13 | taintedUnicodeScalars |
| subscript.swift:13:15:13:22 | call to source() : | subscript.swift:13:15:13:25 | ...[...] |
| subscript.swift:14:15:14:23 | call to source2() : | subscript.swift:14:15:14:26 | ...[...] |
| try.swift:9:17:9:24 | call to source() : | try.swift:9:13:9:24 | try ... |
@@ -541,6 +555,7 @@ edges
| webview.swift:137:34:137:41 | call to source() : | webview.swift:66:5:66:126 | [summary param] 0 in init(source:injectionTime:forMainFrameOnly:in:) : |
| webview.swift:137:34:137:41 | call to source() : | webview.swift:137:13:137:113 | call to init(source:injectionTime:forMainFrameOnly:in:) : |
nodes
| data.swift:24:5:24:29 | [summary param] 0 in init(_:) : | semmle.label | [summary param] 0 in init(_:) : |
| data.swift:25:2:25:66 | [summary param] 0 in init(base64Encoded:options:) : | semmle.label | [summary param] 0 in init(base64Encoded:options:) : |
| data.swift:26:2:26:61 | [summary param] 0 in init(buffer:) : | semmle.label | [summary param] 0 in init(buffer:) : |
| data.swift:27:2:27:62 | [summary param] 0 in init(buffer:) : | semmle.label | [summary param] 0 in init(buffer:) : |
@@ -576,6 +591,13 @@ nodes
| data.swift:62:2:62:58 | [summary param] this in shuffled(using:) : | semmle.label | [summary param] this in shuffled(using:) : |
| data.swift:63:2:63:123 | [summary param] this in trimmingPrefix(_:) : | semmle.label | [summary param] this in trimmingPrefix(_:) : |
| data.swift:64:2:64:72 | [summary param] this in trimmingPrefix(while:) : | semmle.label | [summary param] this in trimmingPrefix(while:) : |
| data.swift:81:20:81:51 | call to init(_:) : | semmle.label | call to init(_:) : |
| data.swift:81:25:81:47 | .utf8 : | semmle.label | .utf8 : |
| data.swift:81:26:81:33 | call to source() : | semmle.label | call to source() : |
| data.swift:82:21:82:37 | call to init(_:) : | semmle.label | call to init(_:) : |
| data.swift:82:26:82:26 | dataTainted : | semmle.label | dataTainted : |
| data.swift:85:12:85:12 | dataTainted | semmle.label | dataTainted |
| data.swift:86:12:86:12 | dataTainted2 | semmle.label | dataTainted2 |
| data.swift:89:21:89:71 | call to init(base64Encoded:options:) : | semmle.label | call to init(base64Encoded:options:) : |
| data.swift:89:41:89:48 | call to source() : | semmle.label | call to source() : |
| data.swift:90:12:90:12 | dataTainted3 | semmle.label | dataTainted3 |
@@ -734,6 +756,7 @@ nodes
| file://:0:0:0:0 | [summary] to write: return (return) in flatMap(_:) : | semmle.label | [summary] to write: return (return) in flatMap(_:) : |
| file://:0:0:0:0 | [summary] to write: return (return) in flatMap(_:) : | semmle.label | [summary] to write: return (return) in flatMap(_:) : |
| file://:0:0:0:0 | [summary] to write: return (return) in forProperty(_:) : | semmle.label | [summary] to write: return (return) in forProperty(_:) : |
| file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | semmle.label | [summary] to write: return (return) in init(_:) : |
| file://:0:0:0:0 | [summary] to write: return (return) in init(base64Encoded:options:) : | semmle.label | [summary] to write: return (return) in init(base64Encoded:options:) : |
| file://:0:0:0:0 | [summary] to write: return (return) in init(base64Encoded:options:) : | semmle.label | [summary] to write: return (return) in init(base64Encoded:options:) : |
| file://:0:0:0:0 | [summary] to write: return (return) in init(base64Encoded:options:) : | semmle.label | [summary] to write: return (return) in init(base64Encoded:options:) : |
@@ -940,6 +963,15 @@ nodes
| string.swift:35:13:35:23 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
| string.swift:36:13:36:23 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
| string.swift:39:13:39:29 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
| string.swift:74:17:74:25 | call to source2() : | semmle.label | call to source2() : |
| string.swift:85:13:85:21 | .description | semmle.label | .description |
| string.swift:88:13:88:21 | .debugDescription | semmle.label | .debugDescription |
| string.swift:121:17:121:25 | call to source2() : | semmle.label | call to source2() : |
| string.swift:122:24:122:32 | call to source2() : | semmle.label | call to source2() : |
| string.swift:123:31:123:39 | call to source2() : | semmle.label | call to source2() : |
| string.swift:126:13:126:13 | tainted | semmle.label | tainted |
| string.swift:127:13:127:13 | taintedCString | semmle.label | taintedCString |
| string.swift:128:13:128:13 | taintedUnicodeScalars | semmle.label | taintedUnicodeScalars |
| subscript.swift:13:15:13:22 | call to source() : | semmle.label | call to source() : |
| subscript.swift:13:15:13:25 | ...[...] | semmle.label | ...[...] |
| subscript.swift:14:15:14:23 | call to source2() : | semmle.label | call to source2() : |
@@ -1139,6 +1171,8 @@ nodes
| webview.swift:138:10:138:10 | c | semmle.label | c |
| webview.swift:139:10:139:12 | .source | semmle.label | .source |
subpaths
| data.swift:81:25:81:47 | .utf8 : | data.swift:24:5:24:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | data.swift:81:20:81:51 | call to init(_:) : |
| data.swift:82:26:82:26 | dataTainted : | data.swift:24:5:24:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | data.swift:82:21:82:37 | call to init(_:) : |
| data.swift:89:41:89:48 | call to source() : | data.swift:25:2:25:66 | [summary param] 0 in init(base64Encoded:options:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(base64Encoded:options:) : | data.swift:89:21:89:71 | call to init(base64Encoded:options:) : |
| data.swift:93:34:93:41 | call to source() : | data.swift:26:2:26:61 | [summary param] 0 in init(buffer:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(buffer:) : | data.swift:93:21:93:73 | call to init(buffer:) : |
| data.swift:95:34:95:41 | call to source() : | data.swift:27:2:27:62 | [summary param] 0 in init(buffer:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(buffer:) : | data.swift:95:21:95:74 | call to init(buffer:) : |
@@ -1260,6 +1294,8 @@ subpaths
| webview.swift:132:34:132:41 | call to source() : | webview.swift:65:5:65:93 | [summary param] 0 in init(source:injectionTime:forMainFrameOnly:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(source:injectionTime:forMainFrameOnly:) : | webview.swift:132:13:132:102 | call to init(source:injectionTime:forMainFrameOnly:) : |
| webview.swift:137:34:137:41 | call to source() : | webview.swift:66:5:66:126 | [summary param] 0 in init(source:injectionTime:forMainFrameOnly:in:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(source:injectionTime:forMainFrameOnly:in:) : | webview.swift:137:13:137:113 | call to init(source:injectionTime:forMainFrameOnly:in:) : |
#select
| data.swift:85:12:85:12 | dataTainted | data.swift:81:26:81:33 | call to source() : | data.swift:85:12:85:12 | dataTainted | result |
| data.swift:86:12:86:12 | dataTainted2 | data.swift:81:26:81:33 | call to source() : | data.swift:86:12:86:12 | dataTainted2 | result |
| data.swift:90:12:90:12 | dataTainted3 | data.swift:89:41:89:48 | call to source() : | data.swift:90:12:90:12 | dataTainted3 | result |
| data.swift:94:12:94:12 | dataTainted4 | data.swift:93:34:93:41 | call to source() : | data.swift:94:12:94:12 | dataTainted4 | result |
| data.swift:96:12:96:12 | dataTainted5 | data.swift:95:34:95:41 | call to source() : | data.swift:96:12:96:12 | dataTainted5 | result |
@@ -1340,6 +1376,11 @@ subpaths
| string.swift:35:13:35:23 | ... .+(_:_:) ... | string.swift:28:17:28:25 | call to source2() : | string.swift:35:13:35:23 | ... .+(_:_:) ... | result |
| string.swift:36:13:36:23 | ... .+(_:_:) ... | string.swift:28:17:28:25 | call to source2() : | string.swift:36:13:36:23 | ... .+(_:_:) ... | result |
| string.swift:39:13:39:29 | ... .+(_:_:) ... | string.swift:28:17:28:25 | call to source2() : | string.swift:39:13:39:29 | ... .+(_:_:) ... | result |
| string.swift:85:13:85:21 | .description | string.swift:74:17:74:25 | call to source2() : | string.swift:85:13:85:21 | .description | result |
| string.swift:88:13:88:21 | .debugDescription | string.swift:74:17:74:25 | call to source2() : | string.swift:88:13:88:21 | .debugDescription | result |
| string.swift:126:13:126:13 | tainted | string.swift:121:17:121:25 | call to source2() : | string.swift:126:13:126:13 | tainted | result |
| string.swift:127:13:127:13 | taintedCString | string.swift:122:24:122:32 | call to source2() : | string.swift:127:13:127:13 | taintedCString | result |
| string.swift:128:13:128:13 | taintedUnicodeScalars | string.swift:123:31:123:39 | call to source2() : | string.swift:128:13:128:13 | taintedUnicodeScalars | result |
| subscript.swift:13:15:13:25 | ...[...] | subscript.swift:13:15:13:22 | call to source() : | subscript.swift:13:15:13:25 | ...[...] | result |
| subscript.swift:14:15:14:26 | ...[...] | subscript.swift:14:15:14:23 | call to source2() : | subscript.swift:14:15:14:26 | ...[...] | result |
| try.swift:9:13:9:24 | try ... | try.swift:9:17:9:24 | call to source() : | try.swift:9:13:9:24 | try ... | result |

View File

@@ -82,8 +82,8 @@ func taintThroughData() {
let dataTainted2 = Data(dataTainted)
sink(arg: dataClean)
sink(arg: dataTainted) // $ MISSING: tainted=81
sink(arg: dataTainted2) // $ MISSING: tainted=81
sink(arg: dataTainted) // $ tainted=81
sink(arg: dataTainted2) // $ tainted=81
// ";Data;true;init(base64Encoded:options:);;;Argument[0];ReturnValue;taint",
let dataTainted3 = Data(base64Encoded: source() as! Data, options: [])

View File

@@ -82,10 +82,10 @@ func taintThroughStringOperations() {
sink(arg: String(repeating: tainted, count: 2)) // $ MISSING: tainted=74
sink(arg: clean.description)
sink(arg: tainted.description) // $ MISSING: tainted=74
sink(arg: tainted.description) // $ tainted=74
sink(arg: clean.debugDescription)
sink(arg: tainted.debugDescription) // $ MISSING: tainted=74
sink(arg: tainted.debugDescription) // $ tainted=74
}
class Data
@@ -111,3 +111,19 @@ func taintThroughData() {
sink(arg: stringClean!)
sink(arg: stringTainted!) // $ MISSING: tainted=100
}
func sink(arg: String.UTF8View) {}
func sink(arg: ContiguousArray<CChar>) {}
func sink(arg: String.UnicodeScalarView) {}
func taintThroughStringFields() {
let clean = ""
let tainted = source2().utf8
let taintedCString = source2().utf8CString
let taintedUnicodeScalars = source2().unicodeScalars
sink(arg: clean)
sink(arg: tainted) // $ tainted=121
sink(arg: taintedCString) // $ tainted=122
sink(arg: taintedUnicodeScalars) // $ tainted=123
}

View File

@@ -1,6 +1,7 @@
edges
| UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : |
| UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : |
| UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : |
| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : |
| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() |
| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : |
@@ -18,6 +19,7 @@ edges
| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:137:25:137:25 | remoteString |
| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString |
| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString |
| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 : |
| UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : | UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL : |
| UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : | UnsafeWebViewFetch.swift:138:47:138:56 | ...! |
| UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : | UnsafeWebViewFetch.swift:139:48:139:57 | ...! |
@@ -29,6 +31,10 @@ edges
| UnsafeWebViewFetch.swift:132:19:132:61 | call to init(string:relativeTo:) : | UnsafeWebViewFetch.swift:141:48:141:58 | ...! |
| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL : | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : |
| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL : | UnsafeWebViewFetch.swift:132:19:132:61 | call to init(string:relativeTo:) : |
| UnsafeWebViewFetch.swift:150:19:150:41 | call to init(_:) : | UnsafeWebViewFetch.swift:152:15:152:15 | remoteData |
| UnsafeWebViewFetch.swift:150:19:150:41 | call to init(_:) : | UnsafeWebViewFetch.swift:154:15:154:15 | remoteData |
| UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 : | UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in init(_:) : |
| UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 : | UnsafeWebViewFetch.swift:150:19:150:41 | call to init(_:) : |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:174:25:174:25 | "..." |
@@ -37,6 +43,7 @@ edges
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:184:25:184:25 | remoteString |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 : |
| UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : | UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL : |
| UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : | UnsafeWebViewFetch.swift:185:47:185:56 | ...! |
| UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : | UnsafeWebViewFetch.swift:186:48:186:57 | ...! |
@@ -48,11 +55,16 @@ edges
| UnsafeWebViewFetch.swift:179:19:179:61 | call to init(string:relativeTo:) : | UnsafeWebViewFetch.swift:188:48:188:58 | ...! |
| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL : | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : |
| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL : | UnsafeWebViewFetch.swift:179:19:179:61 | call to init(string:relativeTo:) : |
| UnsafeWebViewFetch.swift:197:19:197:41 | call to init(_:) : | UnsafeWebViewFetch.swift:199:15:199:15 | remoteData |
| UnsafeWebViewFetch.swift:197:19:197:41 | call to init(_:) : | UnsafeWebViewFetch.swift:201:15:201:15 | remoteData |
| UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 : | UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in init(_:) : |
| UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 : | UnsafeWebViewFetch.swift:197:19:197:41 | call to init(_:) : |
| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData |
| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | UnsafeWebViewFetch.swift:211:25:211:25 | htmlData |
nodes
| UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in init(string:) : | semmle.label | [summary param] 0 in init(string:) : |
| UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : | semmle.label | [summary param] 1 in init(string:relativeTo:) : |
| UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in init(_:) : | semmle.label | [summary param] 0 in init(_:) : |
| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | semmle.label | try ... : |
| UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | semmle.label | call to init(contentsOf:) : |
| UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | semmle.label | try! ... |
@@ -78,7 +90,11 @@ nodes
| UnsafeWebViewFetch.swift:140:47:140:57 | ...! | semmle.label | ...! |
| UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | semmle.label | remoteString |
| UnsafeWebViewFetch.swift:141:48:141:58 | ...! | semmle.label | ...! |
| UnsafeWebViewFetch.swift:150:19:150:41 | call to init(_:) : | semmle.label | call to init(_:) : |
| UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 : | semmle.label | .utf8 : |
| UnsafeWebViewFetch.swift:152:15:152:15 | remoteData | semmle.label | remoteData |
| UnsafeWebViewFetch.swift:153:85:153:94 | ...! | semmle.label | ...! |
| UnsafeWebViewFetch.swift:154:15:154:15 | remoteData | semmle.label | remoteData |
| UnsafeWebViewFetch.swift:154:86:154:95 | ...! | semmle.label | ...! |
| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | semmle.label | call to getRemoteData() : |
| UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | semmle.label | call to getRemoteData() |
@@ -97,18 +113,25 @@ nodes
| UnsafeWebViewFetch.swift:187:47:187:57 | ...! | semmle.label | ...! |
| UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | semmle.label | remoteString |
| UnsafeWebViewFetch.swift:188:48:188:58 | ...! | semmle.label | ...! |
| UnsafeWebViewFetch.swift:197:19:197:41 | call to init(_:) : | semmle.label | call to init(_:) : |
| UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 : | semmle.label | .utf8 : |
| UnsafeWebViewFetch.swift:199:15:199:15 | remoteData | semmle.label | remoteData |
| UnsafeWebViewFetch.swift:200:90:200:99 | ...! | semmle.label | ...! |
| UnsafeWebViewFetch.swift:201:15:201:15 | remoteData | semmle.label | remoteData |
| UnsafeWebViewFetch.swift:201:91:201:100 | ...! | semmle.label | ...! |
| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | semmle.label | call to getRemoteData() : |
| UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | semmle.label | htmlData |
| UnsafeWebViewFetch.swift:211:25:211:25 | htmlData | semmle.label | htmlData |
| file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | semmle.label | [summary] to write: return (return) in init(_:) : |
| file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | semmle.label | [summary] to write: return (return) in init(string:) : |
| file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | semmle.label | [summary] to write: return (return) in init(string:relativeTo:) : |
subpaths
| UnsafeWebViewFetch.swift:131:30:131:30 | remoteString : | UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | UnsafeWebViewFetch.swift:131:18:131:42 | call to init(string:) : |
| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL : | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | UnsafeWebViewFetch.swift:132:19:132:61 | call to init(string:relativeTo:) : |
| UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 : | UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | UnsafeWebViewFetch.swift:150:19:150:41 | call to init(_:) : |
| UnsafeWebViewFetch.swift:178:30:178:30 | remoteString : | UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in init(string:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:) : | UnsafeWebViewFetch.swift:178:18:178:42 | call to init(string:) : |
| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL : | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in init(string:relativeTo:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(string:relativeTo:) : | UnsafeWebViewFetch.swift:179:19:179:61 | call to init(string:relativeTo:) : |
| UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 : | UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in init(_:) : | file://:0:0:0:0 | [summary] to write: return (return) in init(_:) : | UnsafeWebViewFetch.swift:197:19:197:41 | call to init(_:) : |
#select
| UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | UnsafeWebViewFetch.swift:103:30:103:84 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | Tainted data is used in a WebView fetch without restricting the base URL. |
| UnsafeWebViewFetch.swift:106:25:106:25 | data | UnsafeWebViewFetch.swift:105:18:105:72 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:106:25:106:25 | data | Tainted data is used in a WebView fetch without restricting the base URL. |
@@ -119,10 +142,12 @@ subpaths
| UnsafeWebViewFetch.swift:127:25:127:25 | "..." | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:127:25:127:25 | "..." | Tainted data is used in a WebView fetch without restricting the base URL. |
| UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. |
| UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. |
| UnsafeWebViewFetch.swift:154:15:154:15 | remoteData | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:154:15:154:15 | remoteData | Tainted data is used in a WebView fetch with a tainted base URL. |
| UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. |
| UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. |
| UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. |
| UnsafeWebViewFetch.swift:174:25:174:25 | "..." | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:174:25:174:25 | "..." | Tainted data is used in a WebView fetch without restricting the base URL. |
| UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. |
| UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. |
| UnsafeWebViewFetch.swift:201:15:201:15 | remoteData | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:201:15:201:15 | remoteData | Tainted data is used in a WebView fetch with a tainted base URL. |
| UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | UnsafeWebViewFetch.swift:94:14:94:37 | call to init(contentsOf:) : | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | Tainted data is used in a WebView fetch without restricting the base URL. |

View File

@@ -151,7 +151,7 @@ func testUIWebView() {
webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local
webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified
webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local
webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // BAD [NOT DETECTED]
webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // BAD
}
func testWKWebView() {
@@ -198,7 +198,7 @@ func testWKWebView() {
webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local
webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified
webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local
webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // BAD [NOT DETECTED]
webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // BAD
}
func testQHelpExamples() {