--- a/cnd.highlight/src/org/netbeans/modules/cnd/highlight/security/Bundle.properties +++ a/cnd.highlight/src/org/netbeans/modules/cnd/highlight/security/Bundle.properties @@ -0,0 +1,49 @@ +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 2015 Oracle and/or its affiliates. All rights reserved. +# +# Oracle and Java are registered trademarks of Oracle and/or its affiliates. +# Other names may be trademarks of their respective owners. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common +# Development and Distribution License("CDDL") (collectively, the +# "License"). You may not use this file except in compliance with the +# License. You can obtain a copy of the License at +# http://www.netbeans.org/cddl-gplv2.html +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the +# specific language governing permissions and limitations under the +# License. When distributing the software, include this License Header +# Notice in each file and include the License file at +# nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the GPL Version 2 section of the License file that +# accompanied this code. If applicable, add the following below the +# License Header, with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# If you wish your version of this file to be governed by only the CDDL +# or only the GPL Version 2, indicate your decision by adding +# "[Contributor] elects to include this software in this distribution +# under the [CDDL or GPL Version 2] license." If you do not indicate a +# single choice of license, a recipient has the option to distribute +# your version of this file under either the CDDL, the GPL Version 2 or +# to extend the choice of license to its licensees as provided above. +# However, if you add GPL Version 2 code and therefore, elected the GPL +# Version 2 license, then the option applies only if the new code is +# made subject to such option by the copyright holder. +# +# Contributor(s): +# +# Portions Copyrighted 2015 Sun Microsystems, Inc. + +SecurityCheck_NAME=Security issues +SecurityCheck_DESCRIPTION=Possible security flaws +DisableHint=Configure Hints + +#AvoidFunctionUsage +UsingUnsafeFunctions.Unsafe.name=C library functions: highly unsafe +UsingUnsafeFunctions.Unsafe.description=These function cannot be used securely so it should not to be used under any circumstances +UsingUnsafeFunctions.Avoid.name=C library functions: difficult to use securely +UsingUnsafeFunctions.Avoid.description=Use should be avoided as this function is difficult to use securely --- a/cnd.highlight/src/org/netbeans/modules/cnd/highlight/security/Checks.java +++ a/cnd.highlight/src/org/netbeans/modules/cnd/highlight/security/Checks.java @@ -0,0 +1,138 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2015 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2015 Sun Microsystems, Inc. + */ +package org.netbeans.modules.cnd.highlight.security; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.netbeans.modules.cnd.api.model.CsmFile; +import org.netbeans.modules.cnd.api.model.CsmFunction; +import org.netbeans.modules.cnd.api.model.CsmInclude; +import org.netbeans.modules.cnd.api.model.services.CsmFileInfoQuery; +import org.openide.util.Exceptions; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +/** + * + * @author Danila Sergeyev + */ +public class Checks { + private static final String levelTagName = "level"; // NOI18N + private static final String[] levelTagAttributes = {"value"}; // NOI18N + private static final String functionTagName = "function"; // NOI18N + private static final String[] functionTagAttributes = {"name", "header"}; // NOI18N + + public enum Level { + AVOID, + UNSAFE + } + + private final Map unsecureFunctions; + private final String level; + + private Checks(Level level) { + if (level == Level.AVOID) { + this.level = "avoid"; // NOI18N + } else { + this.level = "unsafe"; // NOI18N + } + unsecureFunctions = new HashMap<>(); + processXml(); + } + + private void processXml() { + try { + Document doc = DocumentBuilderFactory.newInstance() + .newDocumentBuilder() + .parse(Checks.class.getResourceAsStream("Checks.xml")); // NOI18N + + NodeList levelNodes = doc.getElementsByTagName(levelTagName); + for (int i = 0, ilimit = levelNodes.getLength(); i < ilimit; i++) { + Node levelNode = levelNodes.item(i); + if (levelNode.getNodeType() == Node.ELEMENT_NODE) { + Element levelElement = (Element) levelNode; + String value = levelElement.getAttribute(levelTagAttributes[0]); + if (value != null && value.equals(level)) { + NodeList nodes = levelNode.getChildNodes(); + for (int j = 0, jlimit = nodes.getLength(); j < jlimit; j++) { + Node node = nodes.item(j); + if (node.getNodeName().equals(functionTagName) && node.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) node; + String name = element.getAttribute(functionTagAttributes[0]); + String header = element.getAttribute(functionTagAttributes[1]); + unsecureFunctions.put(name, header); + } + } + } + } + } + + } catch (ParserConfigurationException | SAXException | IOException ex) { + Exceptions.printStackTrace(ex); + } + } + + public static Checks getInstance(Level level) { + return new Checks(level); + } + + public boolean isUnsecuredFunction(CsmFunction function) { + String fname = function.getName().toString(); + String header = unsecureFunctions.get(fname); + if (header != null) { + CsmFile file = function.getContainingFile(); + for (CsmInclude include : CsmFileInfoQuery.getDefault().getIncludeStack(file)) { + if (include.getIncludeName().toString().equals(header)) { + return true; + } + } + } + return false; + } + +} --- a/cnd.highlight/src/org/netbeans/modules/cnd/highlight/security/Checks.xml +++ a/cnd.highlight/src/org/netbeans/modules/cnd/highlight/security/Checks.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + --- a/cnd.highlight/src/org/netbeans/modules/cnd/highlight/security/SecurityCheckProvider.java +++ a/cnd.highlight/src/org/netbeans/modules/cnd/highlight/security/SecurityCheckProvider.java @@ -0,0 +1,185 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2015 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2015 Sun Microsystems, Inc. + */ +package org.netbeans.modules.cnd.highlight.security; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; +import org.netbeans.modules.cnd.api.model.CsmFile; +import org.netbeans.modules.cnd.api.model.services.CsmCacheManager; +import org.netbeans.modules.cnd.api.model.syntaxerr.AbstractCodeAudit; +import org.netbeans.modules.cnd.api.model.syntaxerr.AuditPreferences; +import org.netbeans.modules.cnd.api.model.syntaxerr.CodeAudit; +import org.netbeans.modules.cnd.api.model.syntaxerr.CodeAuditFactory; +import org.netbeans.modules.cnd.api.model.syntaxerr.CodeAuditProvider; +import org.netbeans.modules.cnd.api.model.syntaxerr.CsmErrorProvider; +import org.netbeans.modules.cnd.utils.MIMENames; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ServiceProvider; +import org.openide.util.lookup.ServiceProviders; + +/** + * + * @author Danila Sergeyev + */ +@ServiceProviders({ + @ServiceProvider(service = CsmErrorProvider.class, position = 1200), + @ServiceProvider(service = CodeAuditProvider.class, position = 1300) +}) +public class SecurityCheckProvider extends CsmErrorProvider implements CodeAuditProvider { + public static final String NAME = "SecurityCheck"; //NOI18N + private Collection audits; + private final AuditPreferences myPreferences; + + public static CodeAuditProvider getInstance() { + for(CodeAuditProvider provider : Lookup.getDefault().lookupAll(CodeAuditProvider.class)) { + if (NAME.equals(provider.getName())) { + return provider; + } + } + return null; + } + + public SecurityCheckProvider() { + myPreferences = new AuditPreferences(AuditPreferences.AUDIT_PREFERENCES_ROOT.node(NAME)); + } + + SecurityCheckProvider(Preferences preferences) { + try { + if (preferences.nodeExists(NAME)) { + preferences = preferences.node(NAME); + } + } catch (BackingStoreException ex) { + } + if (preferences.absolutePath().endsWith("/"+NAME)) { //NOI18N + myPreferences = new AuditPreferences(preferences); + } else { + myPreferences = new AuditPreferences(preferences.node(NAME)); + } + } + + @Override + public String getName() { + return NAME; + } + + @Override + public String getDisplayName() { + return NbBundle.getMessage(SecurityCheckProvider.class, "SecurityCheck_NAME"); //NOI18N + } + + @Override + public String getDescription() { + return NbBundle.getMessage(SecurityCheckProvider.class, "SecurityCheck_DESCRIPTION"); //NOI18N + } + + @Override + protected void doGetErrors(CsmErrorProvider.Request request, CsmErrorProvider.Response response) { + CsmFile file = request.getFile(); + if (file != null) { + if (request.isCancelled()) { + return; + } + CsmCacheManager.enter(); + try { + for(CodeAudit audit : getAudits()) { + if (request.isCancelled()) { + return; + } + AbstractCodeAudit engine = (AbstractCodeAudit)audit; + if (engine.isEnabled() && engine.isSupportedEvent(request.getEvent())) { + engine.doGetErrors(request, response); + } + } + } finally { + CsmCacheManager.leave(); + } + } + } + + @Override + public synchronized Collection getAudits() { + if (audits == null) { + List res = new ArrayList<>(); + for(CodeAuditFactory factory : Lookups.forPath(CodeAuditFactory.REGISTRATION_PATH+NAME).lookupAll(CodeAuditFactory.class)) { + res.add(factory.create(myPreferences)); + } + Collections.sort(res, new Comparator(){ + + @Override + public int compare(CodeAudit o1, CodeAudit o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + audits = res; + } + return audits; + } + + @Override + public AuditPreferences getPreferences() { + return myPreferences; + } + + @Override + public String getMimeType() { + return MIMENames.SOURCES_MIME_TYPE; + } + + @Override + public boolean isSupportedEvent(CsmErrorProvider.EditorEvent kind) { + for(CodeAudit audit : getAudits()) { + AbstractCodeAudit engine = (AbstractCodeAudit)audit; + if (engine.isSupportedEvent(kind)) { + return true; + } + } + return false; + } + +} --- a/cnd.highlight/src/org/netbeans/modules/cnd/highlight/security/UsingUnsafeFunctions.java +++ a/cnd.highlight/src/org/netbeans/modules/cnd/highlight/security/UsingUnsafeFunctions.java @@ -0,0 +1,127 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2015 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2015 Sun Microsystems, Inc. + */ +package org.netbeans.modules.cnd.highlight.security; + +import org.netbeans.modules.cnd.analysis.api.AnalyzerResponse; +import org.netbeans.modules.cnd.api.model.CsmFile; +import org.netbeans.modules.cnd.api.model.CsmFunction; +import org.netbeans.modules.cnd.api.model.syntaxerr.AbstractCodeAudit; +import org.netbeans.modules.cnd.api.model.syntaxerr.AuditPreferences; +import org.netbeans.modules.cnd.api.model.syntaxerr.CodeAuditFactory; +import org.netbeans.modules.cnd.api.model.syntaxerr.CsmErrorInfo; +import org.netbeans.modules.cnd.api.model.syntaxerr.CsmErrorProvider; +import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities; +import org.netbeans.modules.cnd.api.model.xref.CsmReference; +import org.netbeans.modules.cnd.api.model.xref.CsmReferenceResolver; +import org.netbeans.modules.cnd.highlight.hints.ErrorInfoImpl; +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author Danila Sergeyev + */ +public class UsingUnsafeFunctions extends AbstractCodeAudit { + private final Checks.Level level; + private static final String avoidName = "UsingUnsafeFunctions.Avoid.name"; // NOI18N + private static final String avoidDescription = "UsingUnsafeFunctions.Avoid.description"; // NOI18N + private static final String unsafeName = "UsingUnsafeFunctions.Unsafe.name"; // NOI18N + private static final String unsafeDescription = "UsingUnsafeFunctions.Unsafe.description"; // NOI18N + + private UsingUnsafeFunctions(Checks.Level level, String id, String name, String description, String defaultSeverity, boolean defaultEnabled, AuditPreferences myPreferences) { + super(id, name, description, defaultSeverity, defaultEnabled, myPreferences); + this.level = level; + } + + @Override + public boolean isSupportedEvent(CsmErrorProvider.EditorEvent kind) { + return kind == CsmErrorProvider.EditorEvent.FileBased; + } + + @Override + public void doGetErrors(CsmErrorProvider.Request request, CsmErrorProvider.Response response) { + CsmFile file = request.getFile(); + if (file != null) { + if (request.isCancelled()) { + return; + } + + for (CsmReference ref : CsmReferenceResolver.getDefault().getReferences(file)) { + if (CsmKindUtilities.isFunction(ref.getReferencedObject())) { + CsmFunction function = (CsmFunction) ref.getReferencedObject(); + if (Checks.getInstance(level).isUnsecuredFunction(function)) { + String message = NbBundle.getMessage(UsingUnsafeFunctions.class + ,(level == Checks.Level.AVOID)?avoidDescription:avoidName); + CsmErrorInfo.Severity severity = toSeverity(minimalSeverity()); + if (response instanceof AnalyzerResponse) { + ((AnalyzerResponse) response).addError(AnalyzerResponse.AnalyzerSeverity.DetectedError, null, file.getFileObject(), + new ErrorInfoImpl(SecurityCheckProvider.NAME, getID(), getName()+"\n"+message, severity, ref.getStartOffset(), ref.getEndOffset())); // NOI18N + } else { + response.addError(new ErrorInfoImpl(SecurityCheckProvider.NAME, getID(), message, severity, ref.getStartOffset(), ref.getEndOffset())); + } + } + } + } + } + } + + @ServiceProvider(path = CodeAuditFactory.REGISTRATION_PATH+SecurityCheckProvider.NAME, service = CodeAuditFactory.class, position = 1300) + public static final class UnsafeFunctionsAuditFactory implements CodeAuditFactory { + @Override + public AbstractCodeAudit create(AuditPreferences preferences) { + String id = NbBundle.getMessage(UsingUnsafeFunctions.class, unsafeName); + String description = NbBundle.getMessage(UsingUnsafeFunctions.class, unsafeDescription); + return new UsingUnsafeFunctions(Checks.Level.UNSAFE, id, id, description, "error", true, preferences); // NOI18N + } + } + + @ServiceProvider(path = CodeAuditFactory.REGISTRATION_PATH+SecurityCheckProvider.NAME, service = CodeAuditFactory.class, position = 1300) + public static final class AvoidFunctionsAuditFactory implements CodeAuditFactory { + @Override + public AbstractCodeAudit create(AuditPreferences preferences) { + String id = NbBundle.getMessage(UsingUnsafeFunctions.class, avoidName); + String description = NbBundle.getMessage(UsingUnsafeFunctions.class, avoidDescription); + return new UsingUnsafeFunctions(Checks.Level.AVOID, id, id, description, "error", true, preferences); // NOI18N + } + } + +}