diff --git a/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/BottomConfidential.java b/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/BottomConfidential.java
new file mode 100644
index 00000000000..79968aa803a
--- /dev/null
+++ b/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/BottomConfidential.java
@@ -0,0 +1,27 @@
+package org.checkerframework.checker.confidential.qual;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.checkerframework.framework.qual.DefaultFor;
+import org.checkerframework.framework.qual.InvisibleQualifier;
+import org.checkerframework.framework.qual.SubtypeOf;
+import org.checkerframework.framework.qual.TargetLocations;
+import org.checkerframework.framework.qual.TypeUseLocation;
+
+/**
+ * The bottom type in the Confidential type system. Programmers should rarely write this type.
+ *
+ * @checker_framework.manual #confidential-checker Confidential Checker
+ * @checker_framework.manual #bottom-type the bottom type
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+@TargetLocations({TypeUseLocation.EXPLICIT_LOWER_BOUND, TypeUseLocation.EXPLICIT_UPPER_BOUND})
+@InvisibleQualifier
+@SubtypeOf({Confidential.class, NonConfidential.class})
+@DefaultFor(value = {TypeUseLocation.LOWER_BOUND})
+public @interface BottomConfidential {}
diff --git a/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/Confidential.java b/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/Confidential.java
new file mode 100644
index 00000000000..cbbaede733d
--- /dev/null
+++ b/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/Confidential.java
@@ -0,0 +1,26 @@
+package org.checkerframework.checker.confidential.qual;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.checkerframework.framework.qual.SubtypeOf;
+
+/**
+ * Denotes a value that will not be exposed to end users or a sink that will not be able to be
+ * accessed by end users.
+ *
+ *
A Confidential value may contain sensitive, private, or otherwise privileged-access
+ * information. Examples include passwords, PII (personally identifiable information), and private
+ * keys.
+ *
+ * @see NonConfidential
+ * @see org.checkerframework.checker.confidential.ConfidentialChecker
+ * @checker_framework.manual #confidential-checker Confidential Checker
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+@SubtypeOf(UnknownConfidential.class)
+public @interface Confidential {}
diff --git a/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/NonConfidential.java b/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/NonConfidential.java
new file mode 100644
index 00000000000..76f9997cc96
--- /dev/null
+++ b/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/NonConfidential.java
@@ -0,0 +1,29 @@
+package org.checkerframework.checker.confidential.qual;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.checkerframework.framework.qual.DefaultFor;
+import org.checkerframework.framework.qual.DefaultQualifierInHierarchy;
+import org.checkerframework.framework.qual.LiteralKind;
+import org.checkerframework.framework.qual.QualifierForLiterals;
+import org.checkerframework.framework.qual.SubtypeOf;
+import org.checkerframework.framework.qual.TypeUseLocation;
+
+/**
+ * Denotes a value that may be exposed to end users, or a location that may be accessed by end
+ * users. NonConfidential locations will never contain sensitive, private, or otherwise
+ * privileged-access information.
+ *
+ * @checker_framework.manual #confidential-checker Confidential Checker
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+@SubtypeOf(UnknownConfidential.class)
+@QualifierForLiterals({LiteralKind.STRING, LiteralKind.PRIMITIVE})
+@DefaultQualifierInHierarchy
+@DefaultFor(value = {TypeUseLocation.LOCAL_VARIABLE, TypeUseLocation.UPPER_BOUND})
+public @interface NonConfidential {}
diff --git a/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/PolyConfidential.java b/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/PolyConfidential.java
new file mode 100644
index 00000000000..e0bc07155f3
--- /dev/null
+++ b/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/PolyConfidential.java
@@ -0,0 +1,20 @@
+package org.checkerframework.checker.confidential.qual;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.checkerframework.framework.qual.PolymorphicQualifier;
+
+/**
+ * A polymorphic qualifier for the Confidential type system.
+ *
+ * @checker_framework.manual #confidential-checker Confidential Checker
+ * @checker_framework.manual #qualifier-polymorphism Qualifier polymorphism
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+@PolymorphicQualifier(UnknownConfidential.class)
+public @interface PolyConfidential {}
diff --git a/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/UnknownConfidential.java b/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/UnknownConfidential.java
new file mode 100644
index 00000000000..f0d356c915a
--- /dev/null
+++ b/checker-qual/src/main/java/org/checkerframework/checker/confidential/qual/UnknownConfidential.java
@@ -0,0 +1,25 @@
+package org.checkerframework.checker.confidential.qual;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.checkerframework.framework.qual.InvisibleQualifier;
+import org.checkerframework.framework.qual.SubtypeOf;
+import org.checkerframework.framework.qual.TargetLocations;
+import org.checkerframework.framework.qual.TypeUseLocation;
+
+/**
+ * Represents a value that might or might not be confidential. This is the top of the Confidential
+ * qualifier hierarchy.
+ *
+ * @checker_framework.manual #confidential-checker Confidential Checker
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
+@TargetLocations({TypeUseLocation.EXPLICIT_LOWER_BOUND, TypeUseLocation.EXPLICIT_UPPER_BOUND})
+@InvisibleQualifier
+@SubtypeOf({})
+public @interface UnknownConfidential {}
diff --git a/checker/src/main/java/org/checkerframework/checker/confidential/AbstractAuthenticationTargetUrlRequestHandler.astub b/checker/src/main/java/org/checkerframework/checker/confidential/AbstractAuthenticationTargetUrlRequestHandler.astub
new file mode 100644
index 00000000000..031a3e20db3
--- /dev/null
+++ b/checker/src/main/java/org/checkerframework/checker/confidential/AbstractAuthenticationTargetUrlRequestHandler.astub
@@ -0,0 +1,27 @@
+package org.springframework.security.web.authentication;
+
+import java.io.IOException;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.core.log.LogMessage;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.DefaultRedirectStrategy;
+import org.springframework.security.web.RedirectStrategy;
+import org.springframework.security.web.util.UrlUtils;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+import org.checkerframework.checker.confidential.qual.UnknownConfidential;
+
+public abstract class AbstractAuthenticationTargetUrlRequestHandler {
+
+ protected void handle(HttpServletRequest request, HttpServletResponse response, @UnknownConfidential Authentication authentication);
+
+ protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response,
+ @UnknownConfidential Authentication authentication);
+}
diff --git a/checker/src/main/java/org/checkerframework/checker/confidential/AlertDialog.astub b/checker/src/main/java/org/checkerframework/checker/confidential/AlertDialog.astub
new file mode 100644
index 00000000000..78e48b6f71a
--- /dev/null
+++ b/checker/src/main/java/org/checkerframework/checker/confidential/AlertDialog.astub
@@ -0,0 +1,131 @@
+package android.app;
+
+import android.annotation.ArrayRes;
+import android.annotation.AttrRes;
+import android.annotation.DrawableRes;
+import android.annotation.StringRes;
+import android.annotation.StyleRes;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.ResourceId;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Message;
+import android.text.Layout;
+import android.text.method.MovementMethod;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import com.android.internal.R;
+import com.android.internal.app.AlertController;
+
+import org.checkerframework.checker.confidential.qual.*;
+
+public class AlertDialog extends Dialog implements DialogInterface {
+
+ protected AlertDialog(@UnknownConfidential Context context);
+
+ protected AlertDialog(@UnknownConfidential Context context, boolean cancelable,
+ @UnknownConfidential OnCancelListener cancelListener);
+
+ protected AlertDialog(@UnknownConfidential Context context, @StyleRes int themeResId);
+
+ AlertDialog(@UnknownConfidential Context context, @StyleRes int themeResId,
+ boolean createContextThemeWrapper);
+
+ static @StyleRes int resolveDialogTheme(@UnknownConfidential Context context,
+ @StyleRes int themeResId);
+
+ public static class Builder {
+ public @UnknownConfidential Builder(@UnknownConfidential Context context);
+
+ public @UnknownConfidential Builder(@UnknownConfidential Context context, int themeResId);
+
+ public @UnknownConfidential Context getContext();
+
+ public @UnknownConfidential Builder setTitle(@StringRes int titleId);
+
+ public @UnknownConfidential Builder setTitle(CharSequence title);
+
+ public @UnknownConfidential Builder setCustomTitle(View customTitleView);
+
+ public @UnknownConfidential Builder setMessage(@StringRes int messageId);
+
+ public @UnknownConfidential Builder setMessage(CharSequence message);
+
+ public @UnknownConfidential Builder setIcon(@DrawableRes int iconId);
+
+ public @UnknownConfidential Builder setIcon(Drawable icon);
+
+ public @UnknownConfidential Builder setIconAttribute(@AttrRes int attrId);
+
+ public @UnknownConfidential Builder setPositiveButton(@StringRes int textId, final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setPositiveButton(CharSequence text, final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setNegativeButton(@StringRes int textId, final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setNegativeButton(CharSequence text, final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setNeutralButton(@StringRes int textId, final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setNeutralButton(CharSequence text, final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setCancelable(boolean cancelable);
+
+ public @UnknownConfidential Builder setOnCancelListener(@UnknownConfidential OnCancelListener onCancelListener);
+
+ public @UnknownConfidential Builder setOnDismissListener(@UnknownConfidential OnDismissListener onDismissListener);
+
+ public @UnknownConfidential Builder setOnKeyListener(@UnknownConfidential OnKeyListener onKeyListener);
+
+ public @UnknownConfidential Builder setItems(@ArrayRes int itemsId, final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setItems(CharSequence[] items, final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setAdapter(final @UnknownConfidential ListAdapter adapter, final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setCursor(final @UnknownConfidential Cursor cursor, final @UnknownConfidential OnClickListener listener,
+ String labelColumn);
+
+ public @UnknownConfidential Builder setMultiChoiceItems(@ArrayRes int itemsId, boolean[] checkedItems,
+ final @UnknownConfidential OnMultiChoiceClickListener listener);
+
+ public @UnknownConfidential Builder setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems,
+ final @UnknownConfidential OnMultiChoiceClickListener listener);
+
+ public @UnknownConfidential Builder setMultiChoiceItems(@UnknownConfidential Cursor cursor, String isCheckedColumn, String labelColumn,
+ final @UnknownConfidential OnMultiChoiceClickListener listener);
+
+ public @UnknownConfidential Builder setSingleChoiceItems(@ArrayRes int itemsId, int checkedItem,
+ final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setSingleChoiceItems(@UnknownConfidential Cursor cursor, int checkedItem, String labelColumn,
+ final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setSingleChoiceItems(CharSequence[] items, int checkedItem, final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setSingleChoiceItems(@UnknownConfidential ListAdapter adapter, int checkedItem, final @UnknownConfidential OnClickListener listener);
+
+ public @UnknownConfidential Builder setOnItemSelectedListener(final @UnknownConfidential AdapterView.OnItemSelectedListener listener);
+
+ public @UnknownConfidential Builder setView(int layoutResId);
+
+ public @UnknownConfidential Builder setView(View view);
+
+ @UnsupportedAppUsage
+ public @UnknownConfidential Builder setRecycleOnMeasureEnabled(boolean enabled);
+
+ public @UnknownConfidential AlertDialog create();
+
+ public @UnknownConfidential AlertDialog show();
+ }
+}
diff --git a/checker/src/main/java/org/checkerframework/checker/confidential/AndroidLog.astub b/checker/src/main/java/org/checkerframework/checker/confidential/AndroidLog.astub
new file mode 100644
index 00000000000..e95219dd955
--- /dev/null
+++ b/checker/src/main/java/org/checkerframework/checker/confidential/AndroidLog.astub
@@ -0,0 +1,50 @@
+package android.util;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.DeadSystemException;
+import com.android.internal.os.RuntimeInit;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.LineBreakBufferedWriter;
+import dalvik.annotation.optimization.FastNative;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.UnknownHostException;
+
+import org.checkerframework.checker.confidential.qual.*;
+
+public final class Log {
+
+ public static int d(@Nullable String tag, @Nullable String msg, @Nullable @UnknownConfidential Throwable tr);
+
+ public static int w(@Nullable String tag, @Nullable String msg, @Nullable @UnknownConfidential Throwable tr);
+
+ public static int w(@Nullable String tag, @Nullable @UnknownConfidential Throwable tr);
+
+ public static int v(@Nullable String tag, @Nullable String msg, @Nullable @UnknownConfidential Throwable tr);
+
+ public static int i(@Nullable String tag, @Nullable String msg, @Nullable @UnknownConfidential Throwable tr);
+
+ public static int e(@Nullable String tag, @Nullable String msg, @Nullable @UnknownConfidential Throwable tr);
+
+ public static int wtf(@Nullable String tag, @NonNull @UnknownConfidential Throwable tr);
+
+ public static int wtf(@Nullable String tag, @Nullable String msg, @Nullable @UnknownConfidential Throwable tr);
+
+ static int wtf(int logId, @Nullable String tag, @Nullable String msg, @Nullable @UnknownConfidential Throwable tr,
+ boolean localStack, boolean system);
+
+ @NonNull
+ public static @UnknownConfidential TerribleFailureHandler setWtfHandler(@NonNull @UnknownConfidential TerribleFailureHandler handler);
+
+ @NonNull
+ public static String getStackTraceString(@Nullable @UnknownConfidential Throwable tr);
+
+ public static int printlns(int bufID, int priority, @Nullable String tag, @NonNull String msg,
+ @Nullable @UnknownConfidential Throwable tr);
+}
diff --git a/checker/src/main/java/org/checkerframework/checker/confidential/ApacheLog.astub b/checker/src/main/java/org/checkerframework/checker/confidential/ApacheLog.astub
new file mode 100644
index 00000000000..ab6f380348e
--- /dev/null
+++ b/checker/src/main/java/org/checkerframework/checker/confidential/ApacheLog.astub
@@ -0,0 +1,18 @@
+package org.apache.commons.logging;
+
+import org.checkerframework.checker.confidential.qual.UnknownConfidential;
+
+public interface Log {
+
+ void debug(Object var1, @UnknownConfidential Throwable var2);
+
+ void error(Object var1, @UnknownConfidential Throwable var2);
+
+ void fatal(Object var1, @UnknownConfidential Throwable var2);
+
+ void info(Object var1, @UnknownConfidential Throwable var2);
+
+ void trace(Object var1, @UnknownConfidential Throwable var2);
+
+ void warn(Object var1, @UnknownConfidential Throwable var2);
+}
diff --git a/checker/src/main/java/org/checkerframework/checker/confidential/Authentication.astub b/checker/src/main/java/org/checkerframework/checker/confidential/Authentication.astub
new file mode 100644
index 00000000000..c09af83eb54
--- /dev/null
+++ b/checker/src/main/java/org/checkerframework/checker/confidential/Authentication.astub
@@ -0,0 +1,21 @@
+package org.springframework.security.core;
+
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Collection;
+
+import org.checkerframework.checker.confidential.qual.Confidential;
+
+public interface Authentication extends Principal, Serializable {
+ Collection extends GrantedAuthority> getAuthorities();
+
+ @Confidential Object getCredentials();
+
+ Object getDetails();
+
+ Object getPrincipal();
+
+ boolean isAuthenticated();
+
+ void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
+}
diff --git a/checker/src/main/java/org/checkerframework/checker/confidential/Claims.astub b/checker/src/main/java/org/checkerframework/checker/confidential/Claims.astub
new file mode 100644
index 00000000000..2526fa1c289
--- /dev/null
+++ b/checker/src/main/java/org/checkerframework/checker/confidential/Claims.astub
@@ -0,0 +1,36 @@
+import org.checkerframework.checker.confidential.qual.PolyConfidential;
+
+import java.util.Date;
+import java.util.Map;
+
+public interface Claims extends Map<@PolyConfidential String, @PolyConfidential Object>, ClaimsMutator {
+ String getIssuer();
+
+ Claims setIssuer(String var1);
+
+ String getSubject(@PolyConfidential Claims this);
+
+ Claims setSubject(String var1);
+
+ String getAudience(@PolyConfidential Claims this);
+
+ Claims setAudience(String var1);
+
+ Date getExpiration(@PolyConfidential Claims this);
+
+ Claims setExpiration(Date var1);
+
+ Date getNotBefore(@PolyConfidential Claims this);
+
+ Claims setNotBefore(Date var1);
+
+ Date getIssuedAt(@PolyConfidential Claims this);
+
+ Claims setIssuedAt(Date var1);
+
+ String getId(@PolyConfidential Claims this);
+
+ Claims setId(String var1);
+
+ T get(String var1, Class var2);
+}
diff --git a/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialAnnotatedTypeFactory.java b/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialAnnotatedTypeFactory.java
new file mode 100644
index 00000000000..7953520d82d
--- /dev/null
+++ b/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialAnnotatedTypeFactory.java
@@ -0,0 +1,140 @@
+package org.checkerframework.checker.confidential;
+
+import com.sun.source.tree.MethodInvocationTree;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import org.checkerframework.checker.confidential.qual.BottomConfidential;
+import org.checkerframework.checker.confidential.qual.Confidential;
+import org.checkerframework.checker.confidential.qual.NonConfidential;
+import org.checkerframework.checker.confidential.qual.UnknownConfidential;
+import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
+import org.checkerframework.common.basetype.BaseTypeChecker;
+import org.checkerframework.framework.flow.CFAbstractAnalysis;
+import org.checkerframework.framework.flow.CFStore;
+import org.checkerframework.framework.flow.CFTransfer;
+import org.checkerframework.framework.flow.CFValue;
+import org.checkerframework.framework.type.AnnotatedTypeFactory;
+import org.checkerframework.framework.type.AnnotatedTypeMirror;
+import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator;
+import org.checkerframework.framework.type.treeannotator.TreeAnnotator;
+import org.checkerframework.javacutil.AnnotationBuilder;
+import org.checkerframework.javacutil.AnnotationMirrorSet;
+import org.checkerframework.javacutil.TreeUtils;
+
+/** Annotated type factory for the Confidential Checker. */
+public class ConfidentialAnnotatedTypeFactory extends BaseAnnotatedTypeFactory {
+
+ /** The {@code @}{@link NonConfidential} annotation mirror. */
+ protected final AnnotationMirror NONCONFIDENTIAL;
+
+ /** The {@code @}{@link Confidential} annotation mirror. */
+ protected final AnnotationMirror CONFIDENTIAL;
+
+ /** The {@code @}{@link UnknownConfidential} annotation mirror. */
+ protected final AnnotationMirror UNKNOWN_CONFIDENTIAL;
+
+ /** The {@code @}{@link BottomConfidential} annotation mirror. */
+ protected final AnnotationMirror BOTTOM_CONFIDENTIAL;
+
+ /** Fully-qualified class name of {@link NonConfidential}. */
+ public static final String NONCONFIDENTIAL_NAME =
+ "org.checkerframework.checker.confidential.qual.NonConfidential";
+
+ /** Fully-qualified class name of {@link Confidential}. */
+ public static final String CONFIDENTIAL_NAME =
+ "org.checkerframework.checker.confidential.qual.Confidential";
+
+ /** Fully-qualified class name of {@link UnknownConfidential}. */
+ public static final String UNKNOWN_CONFIDENTIAL_NAME =
+ "org.checkerframework.checker.confidential.qual.UnknownConfidential";
+
+ /** Fully-qualified class name of {@link BottomConfidential}. */
+ public static final String BOTTOM_CONFIDENTIAL_NAME =
+ "org.checkerframework.checker.confidential.qual.BottomConfidential";
+
+ /** A singleton set containing the {@code @}{@link NonConfidential} annotation mirror. */
+ private final AnnotationMirrorSet setOfNonConfidential;
+
+ /** The Object.toString method. */
+ private final ExecutableElement objectToString =
+ TreeUtils.getMethod("java.lang.Object", "toString", 0, processingEnv);
+
+ /**
+ * Creates a {@link ConfidentialAnnotatedTypeFactory}.
+ *
+ * @param checker the confidential checker
+ */
+ public ConfidentialAnnotatedTypeFactory(BaseTypeChecker checker) {
+ super(checker);
+ this.NONCONFIDENTIAL = AnnotationBuilder.fromClass(getElementUtils(), NonConfidential.class);
+ this.CONFIDENTIAL = AnnotationBuilder.fromClass(getElementUtils(), Confidential.class);
+ this.UNKNOWN_CONFIDENTIAL =
+ AnnotationBuilder.fromClass(getElementUtils(), UnknownConfidential.class);
+ this.BOTTOM_CONFIDENTIAL =
+ AnnotationBuilder.fromClass(getElementUtils(), BottomConfidential.class);
+ this.setOfNonConfidential = AnnotationMirrorSet.singleton(NONCONFIDENTIAL);
+ postInit();
+ }
+
+ @Override
+ protected Set getEnumConstructorQualifiers() {
+ return setOfNonConfidential;
+ }
+
+ @Override
+ public TreeAnnotator createTreeAnnotator() {
+ return new ListTreeAnnotator(
+ super.createTreeAnnotator(),
+ new ConfidentialAnnotatedTypeFactory.ConfidentialTreeAnnotator(this));
+ }
+
+ @Override
+ public CFTransfer createFlowTransferFunction(
+ CFAbstractAnalysis analysis) {
+ return new ConfidentialTransfer(analysis);
+ }
+
+ /**
+ * A TreeAnnotator to enforce certain toString return type rules:
+ *
+ *
+ *
toString(@NonConfidential this) can return @NonConfidential or @Confidential String
+ *
toString(@Confidential this) can return @Confidential String
+ *
+ */
+ private class ConfidentialTreeAnnotator extends TreeAnnotator {
+ /**
+ * Creates a {@link ConfidentialAnnotatedTypeFactory.ConfidentialTreeAnnotator}
+ *
+ * @param atypeFactory the annotated type factory
+ */
+ public ConfidentialTreeAnnotator(AnnotatedTypeFactory atypeFactory) {
+ super(atypeFactory);
+ }
+
+ /**
+ * Visits a method invocation node. Enforces specific type-checking rules for Object.toString()
+ * that allow a @NonConfidential Object to return a @NonConfidential String.
+ *
+ *
Supplements the @Confidential String return in Object.toString() to cover all secure use
+ * cases, i.e. all cases covered by a @PolyConfidential receiver and return excepting
+ * a @NonConfidential String from @Confidential receivers.
+ *
+ * @param tree an AST node representing a method call
+ * @param type the type obtained from tree
+ */
+ @Override
+ public Void visitMethodInvocation(MethodInvocationTree tree, AnnotatedTypeMirror type) {
+ if (TreeUtils.isMethodInvocation(tree, objectToString, processingEnv)) {
+ AnnotatedTypeMirror receiver = getReceiverType(tree);
+ if (receiver.hasPrimaryAnnotation(NONCONFIDENTIAL)) {
+ type.replaceAnnotation(NONCONFIDENTIAL);
+ } else {
+ type.replaceAnnotation(CONFIDENTIAL);
+ }
+ }
+ return super.visitMethodInvocation(tree, type);
+ }
+ }
+}
diff --git a/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialChecker.java b/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialChecker.java
new file mode 100644
index 00000000000..53f14ebd8a9
--- /dev/null
+++ b/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialChecker.java
@@ -0,0 +1,38 @@
+package org.checkerframework.checker.confidential;
+
+import org.checkerframework.common.basetype.BaseTypeChecker;
+import org.checkerframework.framework.qual.StubFiles;
+import org.checkerframework.framework.source.SuppressWarningsPrefix;
+
+/**
+ * A type-checker plug-in for the Confidential type system qualifier that finds (and verifies the
+ * absence of) information leakage bugs.
+ *
+ *
It verifies that no confidential values are passed to sensitive sinks. A sensitive sink has a
+ * formal parameter type of {@code @NonConfidential}. One example of a sensitive sink is a method
+ * that displays information to the user.
+ *
+ * @checker_framework.manual #confidential-checker Confidential Checker
+ */
+@StubFiles({
+ "Log4jLogger.astub",
+ "AndroidLog.astub",
+ "Slf4jLogger.astub",
+ "ApacheLog.astub",
+ "AlertDialog.astub",
+ "AbstractAuthenticationTargetUrlRequestHandler.astub",
+ "UsernamePasswordAuthenticationToken.astub",
+ "PasswordEncoder.astub",
+ "HttpServletResponse.astub",
+ "Cookie.astub",
+ "UserDetails.astub",
+ "ExpiringMap.astub",
+ "JwtParser.astub",
+ "Authentication.astub",
+ "Claims.astub"
+})
+@SuppressWarningsPrefix({"confidential"})
+public class ConfidentialChecker extends BaseTypeChecker {
+ /** Creates a ConfidentialChecker. */
+ public ConfidentialChecker() {}
+}
diff --git a/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialTransfer.java b/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialTransfer.java
new file mode 100644
index 00000000000..0482265ed12
--- /dev/null
+++ b/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialTransfer.java
@@ -0,0 +1,115 @@
+package org.checkerframework.checker.confidential;
+
+import javax.lang.model.element.AnnotationMirror;
+import org.checkerframework.dataflow.analysis.TransferInput;
+import org.checkerframework.dataflow.analysis.TransferResult;
+import org.checkerframework.dataflow.cfg.node.Node;
+import org.checkerframework.dataflow.cfg.node.StringConcatenateNode;
+import org.checkerframework.framework.flow.CFAbstractAnalysis;
+import org.checkerframework.framework.flow.CFStore;
+import org.checkerframework.framework.flow.CFTransfer;
+import org.checkerframework.framework.flow.CFValue;
+import org.checkerframework.framework.type.QualifierHierarchy;
+import org.checkerframework.javacutil.AnnotationUtils;
+
+/** The transfer class for the Confidential Checker. */
+public class ConfidentialTransfer extends CFTransfer {
+
+ /** The Confidential type factory. */
+ protected final ConfidentialAnnotatedTypeFactory atypeFactory;
+
+ /** The Confidential qualifier hierarchy. */
+ protected final QualifierHierarchy qualHierarchy;
+
+ /**
+ * Create a new ConfidentialTransfer.
+ *
+ * @param analysis the corresponding analysis
+ */
+ public ConfidentialTransfer(CFAbstractAnalysis analysis) {
+ super(analysis);
+ atypeFactory = (ConfidentialAnnotatedTypeFactory) analysis.getTypeFactory();
+ qualHierarchy = atypeFactory.getQualifierHierarchy();
+ }
+
+ /**
+ * Enforces Confidential String concatenation rules:
+ *
+ *
UnknownConfidential dominates other types in concatenation;
+ *
Non-bottom types dominate BottomConfidential in concatenation.
+ *
+ */
+ @Override
+ public TransferResult visitStringConcatenate(
+ StringConcatenateNode n, TransferInput p) {
+ TransferResult result = super.visitStringConcatenate(n, p);
+ return stringConcatenation(n.getLeftOperand(), n.getRightOperand(), p, result);
+ }
+
+ /**
+ * Determines the type of a string concatenation.
+ *
+ * @param leftOperand the left operand to be concatenated
+ * @param rightOperand the right operand to be concatenated
+ * @param p the input abstract values
+ * @param result the result abstract values
+ * @return the resulting type of the string concatenation operation
+ */
+ public TransferResult stringConcatenation(
+ Node leftOperand,
+ Node rightOperand,
+ TransferInput p,
+ TransferResult result) {
+ AnnotationMirror resultAnno =
+ createAnnotationForStringConcatenation(leftOperand, rightOperand, p);
+ return recreateTransferResult(resultAnno, result);
+ }
+
+ /**
+ * Creates an annotation for a result of string concatenation.
+ *
+ * @param leftOperand the left operand to be concatenated
+ * @param rightOperand the right operand to be concatenated
+ * @param p the input abstract values
+ * @return the resulting AnnotationMirror of the string concatenation operation, or null if either
+ * leftOperand or rightOperand is null or either operand does not belong to the Confidential
+ * hierarchy.
+ */
+ private AnnotationMirror createAnnotationForStringConcatenation(
+ Node leftOperand, Node rightOperand, TransferInput p) {
+ CFValue leftValue = p.getValueOfSubNode(leftOperand);
+ AnnotationMirror leftAnno = getValueAnnotation(leftValue);
+ if (leftAnno == null) {
+ return null;
+ }
+ CFValue rightValue = p.getValueOfSubNode(rightOperand);
+ AnnotationMirror rightAnno = getValueAnnotation(rightValue);
+ if (rightAnno == null) {
+ return null;
+ }
+
+ if (AnnotationUtils.areSameByName(leftAnno, ConfidentialAnnotatedTypeFactory.CONFIDENTIAL_NAME)
+ || AnnotationUtils.areSameByName(
+ rightAnno, ConfidentialAnnotatedTypeFactory.CONFIDENTIAL_NAME)) {
+ return atypeFactory.CONFIDENTIAL;
+ }
+
+ return qualHierarchy.leastUpperBoundShallow(
+ leftAnno, leftOperand.getType(), rightAnno, rightOperand.getType());
+ }
+
+ /**
+ * Returns the annotation in the Confidential type hierarchy for the given value.
+ *
+ * @param cfValue the value
+ * @return the value's AnnotationMirror from the Confidential hierarchy
+ */
+ private AnnotationMirror getValueAnnotation(CFValue cfValue) {
+ return qualHierarchy.findAnnotationInHierarchy(
+ cfValue.getAnnotations(), atypeFactory.UNKNOWN_CONFIDENTIAL);
+ }
+}
diff --git a/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialVisitor.java b/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialVisitor.java
new file mode 100644
index 00000000000..527e11b155b
--- /dev/null
+++ b/checker/src/main/java/org/checkerframework/checker/confidential/ConfidentialVisitor.java
@@ -0,0 +1,50 @@
+package org.checkerframework.checker.confidential;
+
+import com.sun.source.tree.Tree;
+import javax.lang.model.element.ExecutableElement;
+import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey;
+import org.checkerframework.checker.confidential.qual.Confidential;
+import org.checkerframework.checker.formatter.qual.FormatMethod;
+import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
+import org.checkerframework.common.basetype.BaseTypeChecker;
+import org.checkerframework.common.basetype.BaseTypeVisitor;
+import org.checkerframework.framework.type.AnnotatedTypeMirror;
+import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType;
+
+/** Visitor for the {@link ConfidentialChecker}. */
+public class ConfidentialVisitor extends BaseTypeVisitor {
+
+ /**
+ * Creates a {@link ConfidentialVisitor}.
+ *
+ * @param checker the checker that uses this visitor
+ */
+ public ConfidentialVisitor(BaseTypeChecker checker) {
+ super(checker);
+ }
+
+ /**
+ * Don't check that the constructor result is top. Checking that the super() or this() call is a
+ * subtype of the constructor result is sufficient.
+ *
+ *