Search in sources :

Example 1 with PhpLanguageLevel

use of com.jetbrains.php.config.PhpLanguageLevel in project phpinspectionsea by kalessil.

the class NonSecureCryptUsageInspector method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
    return new BasePhpElementVisitor() {

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            final String functionName = reference.getName();
            if (functionName == null || !functionName.equals("crypt")) {
                return;
            }
            final PsiElement[] arguments = reference.getParameters();
            if ((arguments.length != 1 && arguments.length != 2) || !this.isFromRootNamespace(reference)) {
                return;
            }
            /* Case 1: suggest providing blowfish as the 2nd parameter*/
            if (arguments.length == 1) {
                holder.registerProblem(reference, messageWeakSalt);
                return;
            }
            /* try resolving 2nd parameter, skip if failed, it contains injections or length is not as expected */
            final String saltValue = this.resolveSalt(arguments[1]);
            if (null == saltValue || saltValue.length() < 4) {
                return;
            }
            /* Case 2: using $2a$; use $2y$ instead - http://php.net/security/crypt_blowfish.php*/
            if (saltValue.startsWith("$2a$")) {
                holder.registerProblem(reference, messageInsecureSalt, ProblemHighlightType.GENERIC_ERROR);
                return;
            }
            /* Case 3: -> password_hash(PASSWORD_BCRYPT) in PHP 5.5+ */
            final boolean isBlowfish = saltValue.startsWith("$2y$") || saltValue.startsWith("$2x$");
            if (isBlowfish) {
                PhpLanguageLevel php = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
                if (php.compareTo(PhpLanguageLevel.PHP550) >= 0) {
                    holder.registerProblem(reference, messagePasswordHash, ProblemHighlightType.WEAK_WARNING);
                }
            }
        }

        @Nullable
        private String resolveSalt(@NotNull PsiElement expression) {
            /* collect possible value for further analysis */
            final Set<PsiElement> discovered = PossibleValuesDiscoveryUtil.discover(expression);
            if (discovered.size() != 1) {
                discovered.clear();
                return null;
            }
            /* simplify workflow by handling one expression */
            final PsiElement saltExpression = discovered.iterator().next();
            final StringBuilder resolvedSaltValue = new StringBuilder();
            discovered.clear();
            /*  resolve string literals and concatenations */
            PsiElement current = saltExpression;
            while (current instanceof ConcatenationExpression) {
                final ConcatenationExpression concat = (ConcatenationExpression) current;
                final PsiElement right = ExpressionSemanticUtil.getExpressionTroughParenthesis(concat.getRightOperand());
                final StringLiteralExpression part = ExpressionSemanticUtil.resolveAsStringLiteral(right);
                resolvedSaltValue.insert(0, part == null ? "<?>" : part.getContents());
                current = ExpressionSemanticUtil.getExpressionTroughParenthesis(concat.getLeftOperand());
            }
            /* don't forget to add the last element */
            if (null != current) {
                final StringLiteralExpression lastPart = ExpressionSemanticUtil.resolveAsStringLiteral(current);
                resolvedSaltValue.insert(0, null == lastPart ? "<?>" : lastPart.getContents());
            }
            return resolvedSaltValue.toString();
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) StringLiteralExpression(com.jetbrains.php.lang.psi.elements.StringLiteralExpression) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) ConcatenationExpression(com.jetbrains.php.lang.psi.elements.ConcatenationExpression) NotNull(org.jetbrains.annotations.NotNull) PsiElement(com.intellij.psi.PsiElement) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel) NotNull(org.jetbrains.annotations.NotNull)

Example 2 with PhpLanguageLevel

use of com.jetbrains.php.config.PhpLanguageLevel in project phpinspectionsea by kalessil.

the class ExceptionsAnnotatingAndHandlingInspector method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
    return new BasePhpElementVisitor() {

        @Override
        public void visitPhpFinally(@NotNull Finally element) {
            PhpLanguageLevel phpVersion = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
            if (!phpVersion.hasFeature(PhpLanguageFeature.FINALLY)) {
                return;
            }
            final HashSet<PsiElement> processedRegistry = new HashSet<>();
            final HashMap<PhpClass, HashSet<PsiElement>> exceptions = CollectPossibleThrowsUtil.collectNestedAndWorkflowExceptions(element, processedRegistry, holder);
            /* report individual statements */
            if (exceptions.size() > 0) {
                final Set<PsiElement> reportedExpressions = new HashSet<>();
                for (final Set<PsiElement> pool : exceptions.values()) {
                    pool.stream().filter(expression -> !reportedExpressions.contains(expression)).forEach(expression -> {
                        holder.registerProblem(this.getReportingTarget(expression), messageFinallyExceptions, ProblemHighlightType.GENERIC_ERROR);
                        reportedExpressions.add(expression);
                    });
                    pool.clear();
                }
                reportedExpressions.clear();
                exceptions.clear();
            }
            /* report try-blocks */
            if (processedRegistry.size() > 0) {
                processedRegistry.stream().filter(statement -> statement instanceof Try).forEach(statement -> holder.registerProblem(statement.getFirstChild(), messageFinallyExceptions, ProblemHighlightType.GENERIC_ERROR));
                processedRegistry.clear();
            }
        }

        @Override
        public void visitPhpMethod(@NotNull Method method) {
            if (method.isAbstract() || this.isTestContext(method)) {
                return;
            }
            // __toString has magic methods validation, must not raise exceptions
            final PsiElement methodName = NamedElementUtil.getNameIdentifier(method);
            if (null == methodName || method.getName().equals("__toString")) {
                return;
            }
            /* collect announced cases */
            final HashSet<PhpClass> annotatedExceptions = new HashSet<>();
            final boolean hasPhpDoc = method.getDocComment() != null;
            if (!ThrowsResolveUtil.resolveThrownExceptions(method, annotatedExceptions)) {
                return;
            }
            HashSet<PsiElement> processedRegistry = new HashSet<>();
            HashMap<PhpClass, HashSet<PsiElement>> throwsExceptions = CollectPossibleThrowsUtil.collectNestedAndWorkflowExceptions(method, processedRegistry, holder);
            processedRegistry.clear();
            /* exclude annotated exceptions, identify which has not been thrown */
            final Set<PhpClass> annotatedButNotThrownExceptions = new HashSet<>(annotatedExceptions);
            /* release bundled expressions */
            /* actualize un-thrown exceptions registry */
            annotatedExceptions.stream().filter(key -> hasPhpDoc && throwsExceptions.containsKey(key)).forEach(annotated -> {
                /* release bundled expressions */
                throwsExceptions.get(annotated).clear();
                throwsExceptions.remove(annotated);
                /* actualize un-thrown exceptions registry */
                annotatedButNotThrownExceptions.remove(annotated);
            });
            /* do reporting now: exceptions annotated, but not thrown */
            if (REPORT_NON_THROWN_EXCEPTIONS && annotatedButNotThrownExceptions.size() > 0) {
                final List<String> toReport = annotatedButNotThrownExceptions.stream().map(PhpNamedElement::getFQN).collect(Collectors.toList());
                final String message = messagePatternUnthrown.replace("%c%", String.join(", ", toReport));
                holder.registerProblem(methodName, message, ProblemHighlightType.WEAK_WARNING);
                toReport.clear();
            }
            annotatedButNotThrownExceptions.clear();
            /* do reporting now: exceptions thrown but not annotated */
            if (throwsExceptions.size() > 0) {
                /* deeper analysis needed */
                HashMap<PhpClass, HashSet<PsiElement>> unhandledExceptions = new HashMap<>();
                if (!annotatedExceptions.isEmpty() && hasPhpDoc) {
                    /* filter what to report based on annotated exceptions  */
                    for (final PhpClass annotated : annotatedExceptions) {
                        for (final Map.Entry<PhpClass, HashSet<PsiElement>> throwsExceptionsPair : throwsExceptions.entrySet()) {
                            final PhpClass thrown = throwsExceptionsPair.getKey();
                            /* already reported */
                            if (unhandledExceptions.containsKey(thrown)) {
                                continue;
                            }
                            /* check thrown parents, as annotated not processed here */
                            final HashSet<PhpClass> thrownVariants = InterfacesExtractUtil.getCrawlInheritanceTree(thrown, true);
                            if (!thrownVariants.contains(annotated)) {
                                unhandledExceptions.put(thrown, throwsExceptionsPair.getValue());
                                throwsExceptions.put(thrown, null);
                            }
                            thrownVariants.clear();
                        }
                    }
                } else {
                    /* report all, as nothing is annotated */
                    for (Map.Entry<PhpClass, HashSet<PsiElement>> throwsExceptionsPair : throwsExceptions.entrySet()) {
                        final PhpClass thrown = throwsExceptionsPair.getKey();
                        /* already reported */
                        if (unhandledExceptions.containsKey(thrown)) {
                            continue;
                        }
                        unhandledExceptions.put(thrown, throwsExceptionsPair.getValue());
                        throwsExceptions.put(thrown, null);
                    }
                }
                if (unhandledExceptions.size() > 0) {
                    for (final Map.Entry<PhpClass, HashSet<PsiElement>> unhandledExceptionsPair : unhandledExceptions.entrySet()) {
                        final String thrown = unhandledExceptionsPair.getKey().getFQN();
                        final Set<PsiElement> blamedExpressions = unhandledExceptionsPair.getValue();
                        if (!configuration.contains(thrown)) {
                            final String message = messagePattern.replace("%c%", thrown);
                            for (final PsiElement blame : blamedExpressions) {
                                final LocalQuickFix fix = hasPhpDoc ? new MissingThrowAnnotationLocalFix(method, thrown) : null;
                                holder.registerProblem(this.getReportingTarget(blame), message, ProblemHighlightType.WEAK_WARNING, fix);
                            }
                        }
                        blamedExpressions.clear();
                    }
                    unhandledExceptions.clear();
                }
                throwsExceptions.clear();
            }
            annotatedExceptions.clear();
        }

        @NotNull
        private PsiElement getReportingTarget(@NotNull PsiElement expression) {
            PsiElement result = expression;
            if (expression instanceof FunctionReference) {
                final PsiElement nameNode = (PsiElement) ((FunctionReference) expression).getNameNode();
                if (nameNode != null) {
                    result = nameNode;
                }
            } else if (expression instanceof PhpThrow) {
                final PsiElement subject = ((PhpThrow) expression).getArgument();
                if (subject instanceof NewExpression) {
                    final PsiElement reference = ((NewExpression) subject).getClassReference();
                    if (reference != null) {
                        result = reference;
                    }
                }
            }
            return result;
        }
    };
}
Also used : PhpDocComment(com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment) java.util(java.util) com.jetbrains.php.lang.psi.elements(com.jetbrains.php.lang.psi.elements) ThrowsResolveUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.phpDoc.ThrowsResolveUtil) ProblemDescriptor(com.intellij.codeInspection.ProblemDescriptor) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel) PsiElement(com.intellij.psi.PsiElement) Project(com.intellij.openapi.project.Project) PhpProjectConfigurationFacade(com.jetbrains.php.config.PhpProjectConfigurationFacade) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) PsiElementVisitor(com.intellij.psi.PsiElementVisitor) ProblemsHolder(com.intellij.codeInspection.ProblemsHolder) PhpPsiElementFactory(com.jetbrains.php.lang.psi.PhpPsiElementFactory) BasePhpInspection(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpInspection) InterfacesExtractUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.hierarhy.InterfacesExtractUtil) PhpLanguageFeature(com.jetbrains.php.config.PhpLanguageFeature) OptionsComponent(com.kalessil.phpStorm.phpInspectionsEA.options.OptionsComponent) Collectors(java.util.stream.Collectors) SmartPointerManager(com.intellij.psi.SmartPointerManager) CollectPossibleThrowsUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.phpExceptions.CollectPossibleThrowsUtil) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) SmartPsiElementPointer(com.intellij.psi.SmartPsiElementPointer) NamedElementUtil(com.kalessil.phpStorm.phpInspectionsEA.utils.NamedElementUtil) ProblemHighlightType(com.intellij.codeInspection.ProblemHighlightType) NotNull(org.jetbrains.annotations.NotNull) javax.swing(javax.swing) LocalQuickFix(com.intellij.codeInspection.LocalQuickFix) NotNull(org.jetbrains.annotations.NotNull) BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) PsiElement(com.intellij.psi.PsiElement) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel) NotNull(org.jetbrains.annotations.NotNull)

Example 3 with PhpLanguageLevel

use of com.jetbrains.php.config.PhpLanguageLevel in project phpinspectionsea by kalessil.

the class RandomApiMigrationInspector method buildVisitor.

@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
    return new BasePhpElementVisitor() {

        @Override
        public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
            final String functionName = reference.getName();
            if (functionName != null) {
                final PhpLanguageLevel php = PhpProjectConfigurationFacade.getInstance(holder.getProject()).getLanguageLevel();
                String suggestion = getMapping(php).get(functionName);
                if (suggestion != null) {
                    /* random_int needs 2 parameters always, so check if mt_rand can be suggested */
                    if (reference.getParameters().length != 2 && suggestion.equals("random_int")) {
                        if (functionName.equals("rand")) {
                            suggestion = "mt_rand";
                        } else {
                            return;
                        }
                    }
                    final String message = messagePattern.replace("%o%", functionName).replace("%n%", suggestion);
                    holder.registerProblem(reference, message, new TheLocalFix(suggestion));
                }
            }
        }
    };
}
Also used : BasePhpElementVisitor(com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor) FunctionReference(com.jetbrains.php.lang.psi.elements.FunctionReference) NotNull(org.jetbrains.annotations.NotNull) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel) NotNull(org.jetbrains.annotations.NotNull)

Example 4 with PhpLanguageLevel

use of com.jetbrains.php.config.PhpLanguageLevel in project phpinspectionsea by kalessil.

the class ConstantCanBeUsedInspectorTest method testIfFindsOsFamilyPatterns.

public void testIfFindsOsFamilyPatterns() {
    final PhpLanguageLevel level = PhpLanguageLevel.parse("7.3");
    if (level != null && level.getVersionString().equals("7.3")) {
        PhpProjectConfigurationFacade.getInstance(myFixture.getProject()).setLanguageLevel(level);
        myFixture.enableInspections(new ConstantCanBeUsedInspector());
        myFixture.configureByFile("testData/fixtures/api/constants-usage.php-os-family.php");
        myFixture.testHighlighting(true, false, true);
    }
}
Also used : ConstantCanBeUsedInspector(com.kalessil.phpStorm.phpInspectionsEA.inspectors.apiUsage.ConstantCanBeUsedInspector) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel)

Example 5 with PhpLanguageLevel

use of com.jetbrains.php.config.PhpLanguageLevel in project phpinspectionsea by kalessil.

the class JsonEncodingApiUsageInspectorTest method testIfFindsErrorsHandlingPatterns.

public void testIfFindsErrorsHandlingPatterns() {
    final PhpLanguageLevel level = PhpLanguageLevel.parse("7.3");
    if (level != null && level.getVersionString().equals("7.3")) {
        PhpProjectConfigurationFacade.getInstance(myFixture.getProject()).setLanguageLevel(level);
        final JsonEncodingApiUsageInspector inspector = new JsonEncodingApiUsageInspector();
        inspector.HARDEN_DECODING_RESULT_TYPE = false;
        inspector.HARDEN_ERRORS_HANDLING = true;
        myFixture.enableInspections(inspector);
        myFixture.configureByFile("testData/fixtures/api/json-encoding-errors-handling.php");
        myFixture.testHighlighting(true, false, true);
        myFixture.getAllQuickFixes().forEach(fix -> myFixture.launchAction(fix));
        myFixture.setTestDataPath(".");
        myFixture.checkResultByFile("testData/fixtures/api/json-encoding-errors-handling.fixed.php");
    }
}
Also used : JsonEncodingApiUsageInspector(com.kalessil.phpStorm.phpInspectionsEA.inspectors.apiUsage.JsonEncodingApiUsageInspector) PhpLanguageLevel(com.jetbrains.php.config.PhpLanguageLevel)

Aggregations

PhpLanguageLevel (com.jetbrains.php.config.PhpLanguageLevel)17 BasePhpElementVisitor (com.kalessil.phpStorm.phpInspectionsEA.openApi.BasePhpElementVisitor)8 NotNull (org.jetbrains.annotations.NotNull)8 PsiElement (com.intellij.psi.PsiElement)7 FunctionReference (com.jetbrains.php.lang.psi.elements.FunctionReference)4 StringLiteralExpression (com.jetbrains.php.lang.psi.elements.StringLiteralExpression)2 Variable (com.jetbrains.php.lang.psi.elements.Variable)2 LocalQuickFix (com.intellij.codeInspection.LocalQuickFix)1 ProblemDescriptor (com.intellij.codeInspection.ProblemDescriptor)1 ProblemHighlightType (com.intellij.codeInspection.ProblemHighlightType)1 ProblemsHolder (com.intellij.codeInspection.ProblemsHolder)1 Project (com.intellij.openapi.project.Project)1 PsiElementVisitor (com.intellij.psi.PsiElementVisitor)1 PsiWhiteSpace (com.intellij.psi.PsiWhiteSpace)1 SmartPointerManager (com.intellij.psi.SmartPointerManager)1 SmartPsiElementPointer (com.intellij.psi.SmartPsiElementPointer)1 LeafPsiElement (com.intellij.psi.impl.source.tree.LeafPsiElement)1 PhpAccessVariableInstruction (com.jetbrains.php.codeInsight.controlFlow.instructions.PhpAccessVariableInstruction)1 PhpEntryPointInstruction (com.jetbrains.php.codeInsight.controlFlow.instructions.PhpEntryPointInstruction)1 PhpLanguageFeature (com.jetbrains.php.config.PhpLanguageFeature)1