1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.android.lint.parcel 18 19 import com.android.tools.lint.checks.infrastructure.LintDetectorTest 20 import com.android.tools.lint.checks.infrastructure.TestLintTask 21 import com.android.tools.lint.checks.infrastructure.TestMode 22 import com.android.tools.lint.detector.api.Detector 23 import com.android.tools.lint.detector.api.Issue 24 25 @Suppress("UnstableApiUsage") 26 class SaferParcelCheckerTest : LintDetectorTest() { getDetectornull27 override fun getDetector(): Detector = SaferParcelChecker() 28 29 override fun getIssues(): List<Issue> = listOf( 30 SaferParcelChecker.ISSUE_UNSAFE_API_USAGE 31 ) 32 33 override fun lint(): TestLintTask = 34 super.lint() 35 .allowMissingSdk(true) 36 // We don't do partial analysis in the platform 37 .skipTestModes(TestMode.PARTIAL) 38 39 /** Parcel Tests */ 40 41 fun testParcelDetectUnsafeReadSerializable() { 42 lint() 43 .files( 44 java( 45 """ 46 package test.pkg; 47 import android.os.Parcel; 48 import java.io.Serializable; 49 50 public class TestClass { 51 private TestClass(Parcel p) { 52 Serializable ans = p.readSerializable(); 53 } 54 } 55 """ 56 ).indented(), 57 *includes 58 ) 59 .expectIdenticalTestModeOutput(false) 60 .run() 61 .expect( 62 """ 63 src/test/pkg/TestClass.java:7: Warning: Unsafe Parcel.readSerializable() \ 64 API usage [UnsafeParcelApi] 65 Serializable ans = p.readSerializable(); 66 ~~~~~~~~~~~~~~~~~~~~ 67 0 errors, 1 warnings 68 """.addLineContinuation() 69 ) 70 } 71 testParcelDoesNotDetectSafeReadSerializablenull72 fun testParcelDoesNotDetectSafeReadSerializable() { 73 lint() 74 .files( 75 java( 76 """ 77 package test.pkg; 78 import android.os.Parcel; 79 import java.io.Serializable; 80 81 public class TestClass { 82 private TestClass(Parcel p) { 83 String ans = p.readSerializable(null, String.class); 84 } 85 } 86 """ 87 ).indented(), 88 *includes 89 ) 90 .run() 91 .expect("No warnings.") 92 } 93 testParcelDetectUnsafeReadArrayListnull94 fun testParcelDetectUnsafeReadArrayList() { 95 lint() 96 .files( 97 java( 98 """ 99 package test.pkg; 100 import android.os.Parcel; 101 102 public class TestClass { 103 private TestClass(Parcel p) { 104 ArrayList ans = p.readArrayList(null); 105 } 106 } 107 """ 108 ).indented(), 109 *includes 110 ) 111 .run() 112 .expect( 113 """ 114 src/test/pkg/TestClass.java:6: Warning: Unsafe Parcel.readArrayList() API \ 115 usage [UnsafeParcelApi] 116 ArrayList ans = p.readArrayList(null); 117 ~~~~~~~~~~~~~~~~~~~~~ 118 0 errors, 1 warnings 119 """.addLineContinuation() 120 ) 121 } 122 testParcelDoesNotDetectSafeReadArrayListnull123 fun testParcelDoesNotDetectSafeReadArrayList() { 124 lint() 125 .files( 126 java( 127 """ 128 package test.pkg; 129 import android.content.Intent; 130 import android.os.Parcel; 131 132 public class TestClass { 133 private TestClass(Parcel p) { 134 ArrayList<Intent> ans = p.readArrayList(null, Intent.class); 135 } 136 } 137 """ 138 ).indented(), 139 *includes 140 ) 141 .run() 142 .expect("No warnings.") 143 } 144 testParcelDetectUnsafeReadListnull145 fun testParcelDetectUnsafeReadList() { 146 lint() 147 .files( 148 java( 149 """ 150 package test.pkg; 151 import android.content.Intent; 152 import android.os.Parcel; 153 import java.util.List; 154 155 public class TestClass { 156 private TestClass(Parcel p) { 157 List<Intent> list = new ArrayList<Intent>(); 158 p.readList(list, null); 159 } 160 } 161 """ 162 ).indented(), 163 *includes 164 ) 165 .run() 166 .expect( 167 """ 168 src/test/pkg/TestClass.java:9: Warning: Unsafe Parcel.readList() API usage \ 169 [UnsafeParcelApi] 170 p.readList(list, null); 171 ~~~~~~~~~~~~~~~~~~~~~~ 172 0 errors, 1 warnings 173 """.addLineContinuation() 174 ) 175 } 176 testDParceloesNotDetectSafeReadListnull177 fun testDParceloesNotDetectSafeReadList() { 178 lint() 179 .files( 180 java( 181 """ 182 package test.pkg; 183 import android.content.Intent; 184 import android.os.Parcel; 185 import java.util.List; 186 187 public class TestClass { 188 private TestClass(Parcel p) { 189 List<Intent> list = new ArrayList<Intent>(); 190 p.readList(list, null, Intent.class); 191 } 192 } 193 """ 194 ).indented(), 195 *includes 196 ) 197 .run() 198 .expect("No warnings.") 199 } 200 testParcelDetectUnsafeReadParcelablenull201 fun testParcelDetectUnsafeReadParcelable() { 202 lint() 203 .files( 204 java( 205 """ 206 package test.pkg; 207 import android.content.Intent; 208 import android.os.Parcel; 209 210 public class TestClass { 211 private TestClass(Parcel p) { 212 Intent ans = p.readParcelable(null); 213 } 214 } 215 """ 216 ).indented(), 217 *includes 218 ) 219 .run() 220 .expect( 221 """ 222 src/test/pkg/TestClass.java:7: Warning: Unsafe Parcel.readParcelable() API \ 223 usage [UnsafeParcelApi] 224 Intent ans = p.readParcelable(null); 225 ~~~~~~~~~~~~~~~~~~~~~~ 226 0 errors, 1 warnings 227 """.addLineContinuation() 228 ) 229 } 230 testParcelDoesNotDetectSafeReadParcelablenull231 fun testParcelDoesNotDetectSafeReadParcelable() { 232 lint() 233 .files( 234 java( 235 """ 236 package test.pkg; 237 import android.content.Intent; 238 import android.os.Parcel; 239 240 public class TestClass { 241 private TestClass(Parcel p) { 242 Intent ans = p.readParcelable(null, Intent.class); 243 } 244 } 245 """ 246 ).indented(), 247 *includes 248 ) 249 .run() 250 .expect("No warnings.") 251 } 252 testParcelDetectUnsafeReadParcelableListnull253 fun testParcelDetectUnsafeReadParcelableList() { 254 lint() 255 .files( 256 java( 257 """ 258 package test.pkg; 259 import android.content.Intent; 260 import android.os.Parcel; 261 import java.util.List; 262 263 public class TestClass { 264 private TestClass(Parcel p) { 265 List<Intent> list = new ArrayList<Intent>(); 266 List<Intent> ans = p.readParcelableList(list, null); 267 } 268 } 269 """ 270 ).indented(), 271 *includes 272 ) 273 .run() 274 .expect( 275 """ 276 src/test/pkg/TestClass.java:9: Warning: Unsafe Parcel.readParcelableList() \ 277 API usage [UnsafeParcelApi] 278 List<Intent> ans = p.readParcelableList(list, null); 279 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 280 0 errors, 1 warnings 281 """.addLineContinuation() 282 ) 283 } 284 testParcelDoesNotDetectSafeReadParcelableListnull285 fun testParcelDoesNotDetectSafeReadParcelableList() { 286 lint() 287 .files( 288 java( 289 """ 290 package test.pkg; 291 import android.content.Intent; 292 import android.os.Parcel; 293 import java.util.List; 294 295 public class TestClass { 296 private TestClass(Parcel p) { 297 List<Intent> list = new ArrayList<Intent>(); 298 List<Intent> ans = 299 p.readParcelableList(list, null, Intent.class); 300 } 301 } 302 """ 303 ).indented(), 304 *includes 305 ) 306 .run() 307 .expect("No warnings.") 308 } 309 testParcelDetectUnsafeReadSparseArraynull310 fun testParcelDetectUnsafeReadSparseArray() { 311 lint() 312 .files( 313 java( 314 """ 315 package test.pkg; 316 import android.content.Intent; 317 import android.os.Parcel; 318 import android.util.SparseArray; 319 320 public class TestClass { 321 private TestClass(Parcel p) { 322 SparseArray<Intent> ans = p.readSparseArray(null); 323 } 324 } 325 """ 326 ).indented(), 327 *includes 328 ) 329 .run() 330 .expect( 331 """ 332 src/test/pkg/TestClass.java:8: Warning: Unsafe Parcel.readSparseArray() API\ 333 usage [UnsafeParcelApi] 334 SparseArray<Intent> ans = p.readSparseArray(null); 335 ~~~~~~~~~~~~~~~~~~~~~~~ 336 0 errors, 1 warnings 337 """.addLineContinuation() 338 ) 339 } 340 testParcelDoesNotDetectSafeReadSparseArraynull341 fun testParcelDoesNotDetectSafeReadSparseArray() { 342 lint() 343 .files( 344 java( 345 """ 346 package test.pkg; 347 import android.content.Intent; 348 import android.os.Parcel; 349 import android.util.SparseArray; 350 351 public class TestClass { 352 private TestClass(Parcel p) { 353 SparseArray<Intent> ans = 354 p.readSparseArray(null, Intent.class); 355 } 356 } 357 """ 358 ).indented(), 359 *includes 360 ) 361 .run() 362 .expect("No warnings.") 363 } 364 testParcelDetectUnsafeReadSArraynull365 fun testParcelDetectUnsafeReadSArray() { 366 lint() 367 .files( 368 java( 369 """ 370 package test.pkg; 371 import android.content.Intent; 372 import android.os.Parcel; 373 374 public class TestClass { 375 private TestClass(Parcel p) { 376 Intent[] ans = p.readArray(null); 377 } 378 } 379 """ 380 ).indented(), 381 *includes 382 ) 383 .run() 384 .expect( 385 """ 386 src/test/pkg/TestClass.java:7: Warning: Unsafe Parcel.readArray() API\ 387 usage [UnsafeParcelApi] 388 Intent[] ans = p.readArray(null); 389 ~~~~~~~~~~~~~~~~~ 390 0 errors, 1 warnings 391 """.addLineContinuation() 392 ) 393 } 394 testParcelDoesNotDetectSafeReadArraynull395 fun testParcelDoesNotDetectSafeReadArray() { 396 lint() 397 .files( 398 java( 399 """ 400 package test.pkg; 401 import android.content.Intent; 402 import android.os.Parcel; 403 404 public class TestClass { 405 private TestClass(Parcel p) { 406 Intent[] ans = p.readArray(null, Intent.class); 407 } 408 } 409 """ 410 ).indented(), 411 *includes 412 ) 413 .run() 414 .expect("No warnings.") 415 } 416 testParcelDetectUnsafeReadParcelableSArraynull417 fun testParcelDetectUnsafeReadParcelableSArray() { 418 lint() 419 .files( 420 java( 421 """ 422 package test.pkg; 423 import android.content.Intent; 424 import android.os.Parcel; 425 426 public class TestClass { 427 private TestClass(Parcel p) { 428 Intent[] ans = p.readParcelableArray(null); 429 } 430 } 431 """ 432 ).indented(), 433 *includes 434 ) 435 .run() 436 .expect( 437 """ 438 src/test/pkg/TestClass.java:7: Warning: Unsafe Parcel.readParcelableArray() API\ 439 usage [UnsafeParcelApi] 440 Intent[] ans = p.readParcelableArray(null); 441 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 442 0 errors, 1 warnings 443 """.addLineContinuation() 444 ) 445 } 446 testParcelDoesNotDetectSafeReadParcelableArraynull447 fun testParcelDoesNotDetectSafeReadParcelableArray() { 448 lint() 449 .files( 450 java( 451 """ 452 package test.pkg; 453 import android.content.Intent; 454 import android.os.Parcel; 455 456 public class TestClass { 457 private TestClass(Parcel p) { 458 Intent[] ans = p.readParcelableArray(null, Intent.class); 459 } 460 } 461 """ 462 ).indented(), 463 *includes 464 ) 465 .run() 466 .expect("No warnings.") 467 } 468 469 /** Bundle Tests */ 470 testBundleDetectUnsafeGetParcelablenull471 fun testBundleDetectUnsafeGetParcelable() { 472 lint() 473 .files( 474 java( 475 """ 476 package test.pkg; 477 import android.content.Intent; 478 import android.os.Bundle; 479 480 public class TestClass { 481 private TestClass(Bundle b) { 482 Intent ans = b.getParcelable("key"); 483 } 484 } 485 """ 486 ).indented(), 487 *includes 488 ) 489 .run() 490 .expect( 491 """ 492 src/test/pkg/TestClass.java:7: Warning: Unsafe Bundle.getParcelable() API usage [UnsafeParcelApi] 493 Intent ans = b.getParcelable("key"); 494 ~~~~~~~~~~~~~~~~~~~~~~ 495 0 errors, 1 warnings 496 """.addLineContinuation() 497 ) 498 } 499 testBundleDoesNotDetectSafeGetParcelablenull500 fun testBundleDoesNotDetectSafeGetParcelable() { 501 lint() 502 .files( 503 java( 504 """ 505 package test.pkg; 506 import android.content.Intent; 507 import android.os.Bundle; 508 509 public class TestClass { 510 private TestClass(Bundle b) { 511 Intent ans = b.getParcelable("key", Intent.class); 512 } 513 } 514 """ 515 ).indented(), 516 *includes 517 ) 518 .run() 519 .expect("No warnings.") 520 } 521 testBundleDetectUnsafeGetParcelableArrayListnull522 fun testBundleDetectUnsafeGetParcelableArrayList() { 523 lint() 524 .files( 525 java( 526 """ 527 package test.pkg; 528 import android.content.Intent; 529 import android.os.Bundle; 530 531 public class TestClass { 532 private TestClass(Bundle b) { 533 ArrayList<Intent> ans = b.getParcelableArrayList("key"); 534 } 535 } 536 """ 537 ).indented(), 538 *includes 539 ) 540 .run() 541 .expect( 542 """ 543 src/test/pkg/TestClass.java:7: Warning: Unsafe Bundle.getParcelableArrayList() API usage [UnsafeParcelApi] 544 ArrayList<Intent> ans = b.getParcelableArrayList("key"); 545 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 546 0 errors, 1 warnings 547 """.addLineContinuation() 548 ) 549 } 550 testBundleDoesNotDetectSafeGetParcelableArrayListnull551 fun testBundleDoesNotDetectSafeGetParcelableArrayList() { 552 lint() 553 .files( 554 java( 555 """ 556 package test.pkg; 557 import android.content.Intent; 558 import android.os.Bundle; 559 560 public class TestClass { 561 private TestClass(Bundle b) { 562 ArrayList<Intent> ans = b.getParcelableArrayList("key", Intent.class); 563 } 564 } 565 """ 566 ).indented(), 567 *includes 568 ) 569 .run() 570 .expect("No warnings.") 571 } 572 testBundleDetectUnsafeGetParcelableArraynull573 fun testBundleDetectUnsafeGetParcelableArray() { 574 lint() 575 .files( 576 java( 577 """ 578 package test.pkg; 579 import android.content.Intent; 580 import android.os.Bundle; 581 582 public class TestClass { 583 private TestClass(Bundle b) { 584 Intent[] ans = b.getParcelableArray("key"); 585 } 586 } 587 """ 588 ).indented(), 589 *includes 590 ) 591 .run() 592 .expect( 593 """ 594 src/test/pkg/TestClass.java:7: Warning: Unsafe Bundle.getParcelableArray() API usage [UnsafeParcelApi] 595 Intent[] ans = b.getParcelableArray("key"); 596 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 597 0 errors, 1 warnings 598 """.addLineContinuation() 599 ) 600 } 601 testBundleDoesNotDetectSafeGetParcelableArraynull602 fun testBundleDoesNotDetectSafeGetParcelableArray() { 603 lint() 604 .files( 605 java( 606 """ 607 package test.pkg; 608 import android.content.Intent; 609 import android.os.Bundle; 610 611 public class TestClass { 612 private TestClass(Bundle b) { 613 Intent[] ans = b.getParcelableArray("key", Intent.class); 614 } 615 } 616 """ 617 ).indented(), 618 *includes 619 ) 620 .run() 621 .expect("No warnings.") 622 } 623 testBundleDetectUnsafeGetSparseParcelableArraynull624 fun testBundleDetectUnsafeGetSparseParcelableArray() { 625 lint() 626 .files( 627 java( 628 """ 629 package test.pkg; 630 import android.content.Intent; 631 import android.os.Bundle; 632 633 public class TestClass { 634 private TestClass(Bundle b) { 635 SparseArray<Intent> ans = b.getSparseParcelableArray("key"); 636 } 637 } 638 """ 639 ).indented(), 640 *includes 641 ) 642 .run() 643 .expect( 644 """ 645 src/test/pkg/TestClass.java:7: Warning: Unsafe Bundle.getSparseParcelableArray() API usage [UnsafeParcelApi] 646 SparseArray<Intent> ans = b.getSparseParcelableArray("key"); 647 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 648 0 errors, 1 warnings 649 """.addLineContinuation() 650 ) 651 } 652 testBundleDoesNotDetectSafeGetSparseParcelableArraynull653 fun testBundleDoesNotDetectSafeGetSparseParcelableArray() { 654 lint() 655 .files( 656 java( 657 """ 658 package test.pkg; 659 import android.content.Intent; 660 import android.os.Bundle; 661 662 public class TestClass { 663 private TestClass(Bundle b) { 664 SparseArray<Intent> ans = b.getSparseParcelableArray("key", Intent.class); 665 } 666 } 667 """ 668 ).indented(), 669 *includes 670 ) 671 .run() 672 .expect("No warnings.") 673 } 674 675 /** Intent Tests */ 676 testIntentDetectUnsafeGetParcelableExtranull677 fun testIntentDetectUnsafeGetParcelableExtra() { 678 lint() 679 .files( 680 java( 681 """ 682 package test.pkg; 683 import android.content.Intent; 684 685 public class TestClass { 686 private TestClass(Intent i) { 687 Intent ans = i.getParcelableExtra("name"); 688 } 689 } 690 """ 691 ).indented(), 692 *includes 693 ) 694 .run() 695 .expect( 696 """ 697 src/test/pkg/TestClass.java:6: Warning: Unsafe Intent.getParcelableExtra() API usage [UnsafeParcelApi] 698 Intent ans = i.getParcelableExtra("name"); 699 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 700 0 errors, 1 warnings 701 """.addLineContinuation() 702 ) 703 } 704 testIntentDoesNotDetectSafeGetParcelableExtranull705 fun testIntentDoesNotDetectSafeGetParcelableExtra() { 706 lint() 707 .files( 708 java( 709 """ 710 package test.pkg; 711 import android.content.Intent; 712 713 public class TestClass { 714 private TestClass(Intent i) { 715 Intent ans = i.getParcelableExtra("name", Intent.class); 716 } 717 } 718 """ 719 ).indented(), 720 *includes 721 ) 722 .run() 723 .expect("No warnings.") 724 } 725 726 727 /** Stubs for classes used for testing */ 728 729 730 private val includes = 731 arrayOf( 732 manifest().minSdk("33"), 733 java( 734 """ 735 package android.os; 736 import java.util.ArrayList; 737 import java.util.List; 738 import java.util.Map; 739 import java.util.HashMap; 740 741 public final class Parcel { 742 // Deprecated 743 public Object[] readArray(ClassLoader loader) { return null; } 744 public ArrayList readArrayList(ClassLoader loader) { return null; } 745 public HashMap readHashMap(ClassLoader loader) { return null; } 746 public void readList(List outVal, ClassLoader loader) {} 747 public void readMap(Map outVal, ClassLoader loader) {} 748 public <T extends Parcelable> T readParcelable(ClassLoader loader) { return null; } 749 public Parcelable[] readParcelableArray(ClassLoader loader) { return null; } 750 public Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) { return null; } 751 public <T extends Parcelable> List<T> readParcelableList(List<T> list, ClassLoader cl) { return null; } 752 public Serializable readSerializable() { return null; } 753 public <T> SparseArray<T> readSparseArray(ClassLoader loader) { return null; } 754 755 // Replacements 756 public <T> T[] readArray(ClassLoader loader, Class<T> clazz) { return null; } 757 public <T> ArrayList<T> readArrayList(ClassLoader loader, Class<? extends T> clazz) { return null; } 758 public <K, V> HashMap<K,V> readHashMap(ClassLoader loader, Class<? extends K> clazzKey, Class<? extends V> clazzValue) { return null; } 759 public <T> void readList(List<? super T> outVal, ClassLoader loader, Class<T> clazz) {} 760 public <K, V> void readMap(Map<? super K, ? super V> outVal, ClassLoader loader, Class<K> clazzKey, Class<V> clazzValue) {} 761 public <T> T readParcelable(ClassLoader loader, Class<T> clazz) { return null; } 762 public <T> T[] readParcelableArray(ClassLoader loader, Class<T> clazz) { return null; } 763 public <T> Parcelable.Creator<T> readParcelableCreator(ClassLoader loader, Class<T> clazz) { return null; } 764 public <T> List<T> readParcelableList(List<T> list, ClassLoader cl, Class<T> clazz) { return null; } 765 public <T> T readSerializable(ClassLoader loader, Class<T> clazz) { return null; } 766 public <T> SparseArray<T> readSparseArray(ClassLoader loader, Class<? extends T> clazz) { return null; } 767 } 768 """ 769 ).indented(), 770 java( 771 """ 772 package android.os; 773 import java.util.ArrayList; 774 import java.util.List; 775 import java.util.Map; 776 import java.util.HashMap; 777 778 public final class Bundle { 779 // Deprecated 780 public <T extends Parcelable> T getParcelable(String key) { return null; } 781 public <T extends Parcelable> ArrayList<T> getParcelableArrayList(String key) { return null; } 782 public Parcelable[] getParcelableArray(String key) { return null; } 783 public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(String key) { return null; } 784 785 // Replacements 786 public <T> T getParcelable(String key, Class<T> clazz) { return null; } 787 public <T> ArrayList<T> getParcelableArrayList(String key, Class<? extends T> clazz) { return null; } 788 public <T> T[] getParcelableArray(String key, Class<T> clazz) { return null; } 789 public <T> SparseArray<T> getSparseParcelableArray(String key, Class<? extends T> clazz) { return null; } 790 791 } 792 """ 793 ).indented(), 794 java( 795 """ 796 package android.os; 797 public interface Parcelable {} 798 """ 799 ).indented(), 800 java( 801 """ 802 package android.content; 803 public class Intent implements Parcelable, Cloneable { 804 // Deprecated 805 public <T extends Parcelable> T getParcelableExtra(String name) { return null; } 806 807 // Replacements 808 public <T> T getParcelableExtra(String name, Class<T> clazz) { return null; } 809 810 } 811 """ 812 ).indented(), 813 java( 814 """ 815 package android.util; 816 public class SparseArray<E> implements Cloneable {} 817 """ 818 ).indented(), 819 ) 820 821 // Substitutes "backslash + new line" with an empty string to imitate line continuation addLineContinuationnull822 private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "") 823 } 824