Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
1c0eade9
Unverified
Commit
1c0eade9
authored
Jan 10, 2022
by
Jenn Magder
Committed by
GitHub
Jan 10, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Hide PII from doctor validators for GitHub template (#96250)
parent
4df55087
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
201 additions
and
32 deletions
+201
-32
runner.dart
packages/flutter_tools/lib/runner.dart
+9
-18
doctor.dart
packages/flutter_tools/lib/src/doctor.dart
+43
-4
doctor_validator.dart
packages/flutter_tools/lib/src/doctor_validator.dart
+8
-3
crash_reporting.dart
...ages/flutter_tools/lib/src/reporting/crash_reporting.dart
+3
-2
doctor_test.dart
...utter_tools/test/commands.shard/hermetic/doctor_test.dart
+108
-1
analytics_test.dart
...ages/flutter_tools/test/general.shard/analytics_test.dart
+4
-0
crash_reporting_test.dart
...lutter_tools/test/general.shard/crash_reporting_test.dart
+22
-3
github_template_test.dart
...lutter_tools/test/general.shard/github_template_test.dart
+3
-0
runner_test.dart
.../flutter_tools/test/general.shard/runner/runner_test.dart
+1
-1
No files found.
packages/flutter_tools/lib/runner.dart
View file @
1c0eade9
...
...
@@ -149,11 +149,18 @@ Future<int> _handleToolError(
globals
.
printError
(
'Oops; flutter has exited unexpectedly: "
$error
".'
);
try
{
final
BufferLogger
logger
=
BufferLogger
(
terminal:
globals
.
terminal
,
outputPreferences:
globals
.
outputPreferences
,
);
final
DoctorText
doctorText
=
DoctorText
(
logger
);
final
CrashDetails
details
=
CrashDetails
(
command:
_crashCommand
(
args
),
error:
error
,
stackTrace:
stackTrace
,
doctorText:
await
_doctorText
()
,
doctorText:
doctorText
,
);
final
File
file
=
await
_createLocalCrashReport
(
details
);
await
globals
.
crashReporter
.
informUser
(
details
,
file
);
...
...
@@ -199,7 +206,7 @@ Future<File> _createLocalCrashReport(CrashDetails details) async {
buffer
.
writeln
(
'```
\n
${details.stackTrace}
```
\n
'
);
buffer
.
writeln
(
'## flutter doctor
\n
'
);
buffer
.
writeln
(
'```
\n
${
details.doctorT
ext}
```'
);
buffer
.
writeln
(
'```
\n
${
await details.doctorText.t
ext}
```'
);
try
{
crashFile
.
writeAsStringSync
(
buffer
.
toString
());
...
...
@@ -221,22 +228,6 @@ Future<File> _createLocalCrashReport(CrashDetails details) async {
return
crashFile
;
}
Future
<
String
>
_doctorText
()
async
{
try
{
final
BufferLogger
logger
=
BufferLogger
(
terminal:
globals
.
terminal
,
outputPreferences:
globals
.
outputPreferences
,
);
final
Doctor
doctor
=
Doctor
(
logger:
logger
);
await
doctor
.
diagnose
(
showColor:
false
);
return
logger
.
statusText
;
}
on
Exception
catch
(
error
,
trace
)
{
return
'encountered exception:
$error
\n\n
${trace.toString().trim()}
\n
'
;
}
}
Future
<
int
>
_exit
(
int
code
)
async
{
// Prints the welcome message if needed.
globals
.
flutterUsage
.
printWelcome
();
...
...
packages/flutter_tools/lib/src/doctor.dart
View file @
1c0eade9
...
...
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:meta/meta.dart'
;
import
'package:process/process.dart'
;
import
'android/android_studio_validator.dart'
;
...
...
@@ -290,11 +291,17 @@ class Doctor {
}
/// Print information about the state of installed tooling.
///
/// To exclude personally identifiable information like device names and
/// paths, set [showPii] to false.
Future
<
bool
>
diagnose
({
bool
androidLicenses
=
false
,
bool
verbose
=
true
,
bool
showColor
=
true
,
AndroidLicenseValidator
?
androidLicenseValidator
,
bool
showPii
=
true
,
List
<
ValidatorTask
>?
startedValidatorTasks
,
bool
sendEvent
=
true
,
})
async
{
if
(
androidLicenses
&&
androidLicenseValidator
!=
null
)
{
return
androidLicenseValidator
.
runLicenseManager
();
...
...
@@ -306,7 +313,7 @@ class Doctor {
bool
doctorResult
=
true
;
int
issues
=
0
;
for
(
final
ValidatorTask
validatorTask
in
startValidatorTasks
())
{
for
(
final
ValidatorTask
validatorTask
in
start
edValidatorTasks
??
start
ValidatorTasks
())
{
final
DoctorValidator
validator
=
validatorTask
.
validator
;
final
Status
status
=
_logger
.
startSpinner
();
ValidationResult
result
;
...
...
@@ -334,8 +341,9 @@ class Doctor {
case
ValidationType
.
installed
:
break
;
}
DoctorResultEvent
(
validator:
validator
,
result:
result
).
send
();
if
(
sendEvent
)
{
DoctorResultEvent
(
validator:
validator
,
result:
result
).
send
();
}
final
String
leadingBox
=
showColor
?
result
.
coloredLeadingBox
:
result
.
leadingBox
;
if
(
result
.
statusInfo
!=
null
)
{
...
...
@@ -351,7 +359,7 @@ class Doctor {
int
hangingIndent
=
2
;
int
indent
=
4
;
final
String
indicator
=
showColor
?
message
.
coloredIndicator
:
message
.
indicator
;
for
(
final
String
line
in
'
$indicator
${
message.m
essage}
'
.
split
(
'
\n
'
))
{
for
(
final
String
line
in
'
$indicator
${
showPii ? message.message : message.piiStrippedM
essage}
'
.
split
(
'
\n
'
))
{
_logger
.
printStatus
(
line
,
hangingIndent:
hangingIndent
,
indent:
indent
,
emphasis:
true
);
// Only do hanging indent for the first line.
hangingIndent
=
0
;
...
...
@@ -558,3 +566,34 @@ class DeviceValidator extends DoctorValidator {
}
}
}
/// Wrapper for doctor to run multiple times with PII and without, running the validators only once.
class
DoctorText
{
DoctorText
(
BufferLogger
logger
,
{
@visibleForTesting
Doctor
?
doctor
,
})
:
_doctor
=
doctor
??
Doctor
(
logger:
logger
),
_logger
=
logger
;
final
BufferLogger
_logger
;
final
Doctor
_doctor
;
bool
_sendDoctorEvent
=
true
;
late
final
Future
<
String
>
text
=
_runDiagnosis
(
true
);
late
final
Future
<
String
>
piiStrippedText
=
_runDiagnosis
(
false
);
// Start the validator tasks only once.
late
final
List
<
ValidatorTask
>
_validatorTasks
=
_doctor
.
startValidatorTasks
();
Future
<
String
>
_runDiagnosis
(
bool
showPii
)
async
{
try
{
await
_doctor
.
diagnose
(
showColor:
false
,
startedValidatorTasks:
_validatorTasks
,
showPii:
showPii
,
sendEvent:
_sendDoctorEvent
);
// Do not send the doctor event a second time.
_sendDoctorEvent
=
false
;
final
String
text
=
_logger
.
statusText
;
_logger
.
clear
();
return
text
;
}
on
Exception
catch
(
error
,
trace
)
{
return
'encountered exception:
$error
\n\n
${trace.toString().trim()}
\n
'
;
}
}
}
packages/flutter_tools/lib/src/doctor_validator.dart
View file @
1c0eade9
...
...
@@ -221,22 +221,27 @@ class ValidationMessage {
///
/// The [contextUrl] may be supplied to link to external resources. This
/// is displayed after the informative message in verbose modes.
const
ValidationMessage
(
this
.
message
,
{
this
.
contextUrl
})
:
type
=
ValidationMessageType
.
information
;
const
ValidationMessage
(
this
.
message
,
{
this
.
contextUrl
,
String
?
piiStrippedMessage
})
:
type
=
ValidationMessageType
.
information
,
piiStrippedMessage
=
piiStrippedMessage
??
message
;
/// Create a validation message with information for a failing validator.
const
ValidationMessage
.
error
(
this
.
message
)
const
ValidationMessage
.
error
(
this
.
message
,
{
String
?
piiStrippedMessage
}
)
:
type
=
ValidationMessageType
.
error
,
piiStrippedMessage
=
piiStrippedMessage
??
message
,
contextUrl
=
null
;
/// Create a validation message with information for a partially failing
/// validator.
const
ValidationMessage
.
hint
(
this
.
message
)
const
ValidationMessage
.
hint
(
this
.
message
,
{
String
?
piiStrippedMessage
}
)
:
type
=
ValidationMessageType
.
hint
,
piiStrippedMessage
=
piiStrippedMessage
??
message
,
contextUrl
=
null
;
final
ValidationMessageType
type
;
final
String
?
contextUrl
;
final
String
message
;
/// Optional message with PII stripped, to show instead of [message].
final
String
piiStrippedMessage
;
bool
get
isError
=>
type
==
ValidationMessageType
.
error
;
...
...
packages/flutter_tools/lib/src/reporting/crash_reporting.dart
View file @
1c0eade9
...
...
@@ -12,6 +12,7 @@ import '../base/io.dart';
import
'../base/logger.dart'
;
import
'../base/os.dart'
;
import
'../base/platform.dart'
;
import
'../doctor.dart'
;
import
'../project.dart'
;
import
'github_template.dart'
;
import
'reporting.dart'
;
...
...
@@ -49,7 +50,7 @@ class CrashDetails {
final
String
command
;
final
Object
error
;
final
StackTrace
stackTrace
;
final
String
doctorText
;
final
DoctorText
doctorText
;
}
/// Reports information about the crash to the user.
...
...
@@ -92,7 +93,7 @@ class CrashReporter {
details
.
command
,
details
.
error
,
details
.
stackTrace
,
details
.
doctor
Text
,
await
details
.
doctorText
.
piiStripped
Text
,
);
_logger
.
printStatus
(
'
$gitHubTemplateURL
\n
'
,
wrap:
false
);
}
...
...
packages/flutter_tools/test/commands.shard/hermetic/doctor_test.dart
View file @
1c0eade9
...
...
@@ -313,6 +313,14 @@ void main() {
},
overrides:
<
Type
,
Generator
>{
Usage:
()
=>
testUsage
,
});
testUsingContext
(
'sending events can be skipped'
,
()
async
{
await
FakePassingDoctor
(
logger
).
diagnose
(
verbose:
false
,
sendEvent:
false
);
expect
(
testUsage
.
events
,
isEmpty
);
},
overrides:
<
Type
,
Generator
>{
Usage:
()
=>
testUsage
,
});
});
group
(
'doctor with fake validators'
,
()
{
...
...
@@ -456,6 +464,78 @@ void main() {
'! Doctor found issues in 4 categories.
\n
'
));
});
testUsingContext
(
'validate PII can be hidden'
,
()
async
{
expect
(
await
FakePiiDoctor
(
logger
).
diagnose
(
showPii:
false
),
isTrue
);
expect
(
logger
.
statusText
,
equals
(
'[✓] PII Validator
\n
'
' • Does not contain PII
\n
'
'
\n
'
'• No issues found!
\n
'
));
logger
.
clear
();
// PII shown.
expect
(
await
FakePiiDoctor
(
logger
).
diagnose
(),
isTrue
);
expect
(
logger
.
statusText
,
equals
(
'[✓] PII Validator
\n
'
' • Contains PII path/to/username
\n
'
'
\n
'
'• No issues found!
\n
'
));
});
});
group
(
'doctor diagnosis wrapper'
,
()
{
TestUsage
testUsage
;
BufferLogger
logger
;
setUp
(()
{
testUsage
=
TestUsage
();
logger
=
BufferLogger
.
test
();
});
testUsingContext
(
'PII separated, events only sent once'
,
()
async
{
final
Doctor
fakeDoctor
=
FakePiiDoctor
(
logger
);
final
DoctorText
doctorText
=
DoctorText
(
logger
,
doctor:
fakeDoctor
);
const
String
expectedPiiText
=
'[✓] PII Validator
\n
'
' • Contains PII path/to/username
\n
'
'
\n
'
'• No issues found!
\n
'
;
const
String
expectedPiiStrippedText
=
'[✓] PII Validator
\n
'
' • Does not contain PII
\n
'
'
\n
'
'• No issues found!
\n
'
;
// Run each multiple times to make sure the logger buffer is being cleared,
// and that events are only sent once.
expect
(
await
doctorText
.
text
,
expectedPiiText
);
expect
(
await
doctorText
.
text
,
expectedPiiText
);
expect
(
await
doctorText
.
piiStrippedText
,
expectedPiiStrippedText
);
expect
(
await
doctorText
.
piiStrippedText
,
expectedPiiStrippedText
);
// Only one event sent.
expect
(
testUsage
.
events
,
<
TestUsageEvent
>[
const
TestUsageEvent
(
'doctor-result'
,
'PiiValidator'
,
label:
'installed'
,
),
]);
},
overrides:
<
Type
,
Generator
>{
Usage:
()
=>
testUsage
,
});
testUsingContext
(
'without PII has same text and PII-stripped text'
,
()
async
{
final
Doctor
fakeDoctor
=
FakePassingDoctor
(
logger
);
final
DoctorText
doctorText
=
DoctorText
(
logger
,
doctor:
fakeDoctor
);
final
String
piiText
=
await
doctorText
.
text
;
expect
(
piiText
,
isNotEmpty
);
expect
(
piiText
,
await
doctorText
.
piiStrippedText
);
},
overrides:
<
Type
,
Generator
>{
Usage:
()
=>
testUsage
,
});
});
testUsingContext
(
'validate non-verbose output wrapping'
,
()
async
{
...
...
@@ -535,7 +615,6 @@ void main() {
));
});
group
(
'doctor with grouped validators'
,
()
{
testUsingContext
(
'validate diagnose combines validator output'
,
()
async
{
expect
(
await
FakeGroupedDoctor
(
logger
).
diagnose
(),
isTrue
);
...
...
@@ -666,6 +745,9 @@ class NoOpDoctor implements Doctor {
bool
verbose
=
true
,
bool
showColor
=
true
,
AndroidLicenseValidator
androidLicenseValidator
,
bool
showPii
=
true
,
List
<
ValidatorTask
>
startedValidatorTasks
,
bool
sendEvent
=
true
,
})
async
=>
true
;
@override
...
...
@@ -694,6 +776,18 @@ class PassingValidator extends DoctorValidator {
}
}
class
PiiValidator
extends
DoctorValidator
{
PiiValidator
()
:
super
(
'PII Validator'
);
@override
Future
<
ValidationResult
>
validate
()
async
{
const
List
<
ValidationMessage
>
messages
=
<
ValidationMessage
>[
ValidationMessage
(
'Contains PII path/to/username'
,
piiStrippedMessage:
'Does not contain PII'
),
];
return
const
ValidationResult
(
ValidationType
.
installed
,
messages
);
}
}
class
MissingValidator
extends
DoctorValidator
{
MissingValidator
()
:
super
(
'Missing Validator'
);
...
...
@@ -840,6 +934,19 @@ class FakeQuietDoctor extends Doctor {
}
}
/// A doctor that passes and contains PII that can be hidden.
class
FakePiiDoctor
extends
Doctor
{
FakePiiDoctor
(
Logger
logger
)
:
super
(
logger:
logger
);
List
<
DoctorValidator
>
_validators
;
@override
List
<
DoctorValidator
>
get
validators
{
return
_validators
??=
<
DoctorValidator
>[
PiiValidator
(),
];
}
}
/// A doctor with a validator that throws an exception.
class
FakeCrashingDoctor
extends
Doctor
{
FakeCrashingDoctor
(
Logger
logger
)
:
super
(
logger:
logger
);
...
...
packages/flutter_tools/test/general.shard/analytics_test.dart
View file @
1c0eade9
...
...
@@ -17,6 +17,7 @@ import 'package:flutter_tools/src/commands/build.dart';
import
'package:flutter_tools/src/commands/config.dart'
;
import
'package:flutter_tools/src/commands/doctor.dart'
;
import
'package:flutter_tools/src/doctor.dart'
;
import
'package:flutter_tools/src/doctor_validator.dart'
;
import
'package:flutter_tools/src/features.dart'
;
import
'package:flutter_tools/src/globals.dart'
as
globals
;
import
'package:flutter_tools/src/reporting/reporting.dart'
;
...
...
@@ -368,6 +369,9 @@ class FakeDoctor extends Fake implements Doctor {
bool
verbose
=
true
,
bool
showColor
=
true
,
AndroidLicenseValidator
androidLicenseValidator
,
bool
showPii
=
true
,
List
<
ValidatorTask
>
startedValidatorTasks
,
bool
sendEvent
=
true
,
})
async
{
return
diagnoseSucceeds
;
}
...
...
packages/flutter_tools/test/general.shard/crash_reporting_test.dart
View file @
1c0eade9
...
...
@@ -10,11 +10,13 @@ import 'package:flutter_tools/src/base/io.dart';
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/base/os.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/doctor.dart'
;
import
'package:flutter_tools/src/project.dart'
;
import
'package:flutter_tools/src/reporting/crash_reporting.dart'
;
import
'package:flutter_tools/src/reporting/reporting.dart'
;
import
'package:http/http.dart'
;
import
'package:http/testing.dart'
;
import
'package:test/fake.dart'
;
import
'../src/common.dart'
;
import
'../src/fake_http_client.dart'
;
...
...
@@ -76,7 +78,7 @@ void main() {
expect
(
logger
.
traceText
,
contains
(
'Crash report sent (report ID: test-report-id)'
));
}
testWithoutContext
(
'CrashReporter.informUser provides basic instructions'
,
()
async
{
testWithoutContext
(
'CrashReporter.informUser provides basic instructions
without PII
'
,
()
async
{
final
CrashReporter
crashReporter
=
CrashReporter
(
fileSystem:
fs
,
logger:
logger
,
...
...
@@ -91,12 +93,16 @@ void main() {
command:
'arg1 arg2 arg3'
,
error:
Exception
(
'Dummy exception'
),
stackTrace:
StackTrace
.
current
,
doctorText:
'Fake doctor text'
),
// Spaces are URL query encoded in the output, make it one word to make this test simpler.
doctorText:
FakeDoctorText
(
'Ignored'
,
'NoPIIFakeDoctorText'
),
),
file
,
);
expect
(
logger
.
errorText
,
contains
(
'A crash report has been written to
${file.path}
.'
));
expect
(
logger
.
statusText
,
contains
(
'NoPIIFakeDoctorText'
));
expect
(
logger
.
statusText
,
isNot
(
contains
(
'Ignored'
)));
expect
(
logger
.
statusText
,
contains
(
'https://github.com/flutter/flutter/issues/new'
));
expect
(
logger
.
errorText
,
contains
(
'A crash report has been written to
${file.path}
.'
));
});
testWithoutContext
(
'suppress analytics'
,
()
async
{
...
...
@@ -379,3 +385,16 @@ class CrashingCrashReportSender extends MockClient {
throw
exception
;
});
}
class
FakeDoctorText
extends
Fake
implements
DoctorText
{
FakeDoctorText
(
String
text
,
String
piiStrippedText
)
:
_text
=
text
,
_piiStrippedText
=
piiStrippedText
;
@override
Future
<
String
>
get
text
async
=>
_text
;
final
String
_text
;
@override
Future
<
String
>
get
piiStrippedText
async
=>
_piiStrippedText
;
final
String
_piiStrippedText
;
}
packages/flutter_tools/test/general.shard/github_template_test.dart
View file @
1c0eade9
...
...
@@ -308,4 +308,7 @@ class FakeError extends Error {
StackTrace
get
stackTrace
=>
StackTrace
.
fromString
(
'''
#0 _File.open.<anonymous closure> (dart:io/file_impl.dart:366:9)
#1 _rootRunUnary (dart:async/zone.dart:1141:38)'''
);
@override
String
toString
()
=>
'PII to ignore'
;
}
packages/flutter_tools/test/general.shard/runner/runner_test.dart
View file @
1c0eade9
...
...
@@ -198,7 +198,7 @@ void main() {
expect
(
sentDetails
.
command
,
'flutter crash'
);
expect
(
sentDetails
.
error
,
'an exception % --'
);
expect
(
sentDetails
.
stackTrace
.
toString
(),
contains
(
'CrashingFlutterCommand.runCommand'
));
expect
(
sentDetails
.
doctorT
ext
,
contains
(
'[✓] Flutter'
));
expect
(
await
sentDetails
.
doctorText
.
t
ext
,
contains
(
'[✓] Flutter'
));
},
overrides:
<
Type
,
Generator
>{
Platform:
()
=>
FakePlatform
(
environment:
<
String
,
String
>{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment