Exploring Common Security Vulnerabilities: Unmasking Injection Attacks in ASP.NET Core

Introduction:

Understanding and defending against common security vulnerabilities is paramount in the ever-evolving web application security landscape. Injection attacks are among the most prevalent and potentially devastating of these vulnerabilities. In this article, we'll explore injection attacks in the context of ASP.NET Core and learn how to safeguard our applications.

What Are Injection Attacks?

Injection attacks are a class of security vulnerabilities where malicious data is introduced into an application, often by manipulating user inputs. The attacker aims to trick the application into executing unintended commands or accessing unauthorized data. Common types of injection attacks include SQL injection, command injection, CRLF injection, LDAP injection, and cross-site scripting (XSS).

SQL Injection: A Closer Look

SQL injection (SQLi) is a well-known attack targeting the application's database. Attackers exploit SQLi by injecting malicious SQL code into user inputs, typically in search fields, login forms, or any place where user-provided data is incorporated into SQL queries.

Consider this vulnerable ASP.NET Core code snippet:

public IActionResult GetUser(string username)
{
    // Vulnerable SQL query
    string query = "SELECT * FROM Users WHERE Username = '" + username + "'";
    
    // Execute the query
    var user = _dbContext.Users.FromSqlRaw(query).FirstOrDefault();
    
    // ...
}
 

In this code, the username parameter is directly concatenated into the SQL query string, making it susceptible to SQL injection. An attacker could input malicious SQL code into the username field and compromise the database.

Prevention: Parameterized Queries

To defend against SQL injection, always use parameterized queries or an ORM like Entity Framework Core. Here's a secure version of the code:

public IActionResult GetUser(string username)
{
    var user = _dbContext.Users
        .FromSqlInterpolated($"SELECT * FROM Users WHERE Username = {username}")
        .FirstOrDefault();
    
    // ...
}
 

With parameterized queries, user input is treated as data, not executable code. This approach safeguards your application against SQL injection attacks.

Command Injection: Executing System Commands

Command injection is another attack vector where attackers exploit applications that execute system commands. They inject malicious command-line instructions into user inputs, tricking the application into running unauthorized commands on the host system.

Vulnerable Code Example:

public IActionResult ExecuteCommand(string input)
{
    // Vulnerable command execution
    var process = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            FileName = "cmd.exe",
            Arguments = "/C " + input,
            RedirectStandardOutput = true,
            UseShellExecute = false,
            CreateNoWindow = true
        }
    };
    
    process.Start();
    string result = process.StandardOutput.ReadToEnd();
    process.WaitForExit();
    
    return Content(result);
}
 

In this code, the input variable is directly concatenated into the command, making it susceptible to command injection. An attacker could inject malicious commands, potentially compromising the host system.

Prevention: Input Validation and Safe Execution

To prevent command injection, validate and sanitize user input and use safe execution mechanisms. Here's an example:

public IActionResult ExecuteCommand(string input)
{
    // Safe command execution
    if (!string.IsNullOrWhiteSpace(input) && input.All(char.IsLetterOrDigit))
    {
        // Execute the command
        // ...
    }
    else
    {
        return BadRequest("Invalid input.");
    }
    
    // ...
}
 

By validating and sanitizing user input and restricting command execution to trusted operations, you can mitigate the risk of command injection attacks.

CRLF Injection: Manipulating HTTP Requests

CRLF (Carriage Return Line Feed) injection attacks target HTTP requests and responses. Attackers inject special characters into user inputs to manipulate the headers and content of HTTP requests, potentially causing various issues on the server.

Vulnerable Code Example:

public IActionResult ProcessRequest(string input)
{
    // Vulnerable CRLF injection
    string response = "HTTP/1.1 200 OK\r\n";
    response += "Content-Length: " + input.Length + "\r\n\r\n";
    response += input;
    
    return Content(response);
}
 

In this code, the input variable is incorporated into the HTTP response without proper validation, making it susceptible to CRLF injection. An attacker could inject malicious characters to alter the response headers.

Prevention: Proper Input Validation and Output Encoding

To prevent CRLF injection, validate user inputs and use proper output encoding when constructing HTTP responses. Here's a secure version of the code:

public IActionResult ProcessRequest(string input)
{
    // Secure CRLF prevention
    if (input.Contains("\r") || input.Contains("\n"))
    {
        return BadRequest("Invalid input.");
    }
    
    string response = "HTTP/1.1 200 OK\r\n";
    response += "Content-Length: " + input.Length + "\r\n\r\n";
    response += input;
    
    return Content(response);
}
 

You can mitigate the risk of CRLF injection attacks by properly validating and encoding user inputs.

LDAP Injection: Manipulating LDAP Queries

LDAP (Lightweight Directory Access Protocol) injection attacks target applications interacting with LDAP directories. Attackers inject malicious LDAP statements into user inputs, attempting to manipulate directory queries and potentially access sensitive data.

Vulnerable Code Example:

public IActionResult SearchUser(string username)
{
    // Vulnerable LDAP query
    string query = "(uid=" + username + ")";
    var entry = _ldapConnection.Search(query);
    
    // ...
}
 

In this code, the username parameter is directly concatenated into the LDAP query string, making it susceptible to LDAP injection. An attacker could input malicious LDAP statements, potentially compromising the LDAP directory.

Prevention: Parameterized LDAP Queries

To prevent LDAP injection, use parameterized LDAP queries with proper input validation. Here's a secure version of the code:

public IActionResult SearchUser(string username)
{
    // Secure LDAP query
    var searchFilter = new SearchFilter
    {
        Filter = $"(uid={username})"
    };
    
    var searchRequest = new SearchRequest("ou=users,dc=example,dc=com", searchFilter);
    var response = _ldapConnection.SendRequest(searchRequest) as SearchResponse;
    
    // ...
}
 

By using parameterized LDAP queries and validating user inputs, you can safeguard your application against LDAP injection attacks.

Cross-Site Scripting (XSS) and More

Cross-Site Scripting (XSS) entails attackers injecting client-side code into web applications through input or text areas. This malicious code can then execute in the context of other users' browsers, potentially leading to data theft, session hijacking, and more.

Understanding Cross-Site Scripting (XSS)

In cross-site scripting (XSS) attacks, attackers inject malicious scripts (usually JavaScript) into web pages viewed by other users. These scripts can steal sensitive information, manipulate web content, or perform actions on behalf of the victim user.

Reflected XSS

Reflected XSS occurs when an application includes unvalidated or unencoded user input in the HTML response. Attackers craft URLs containing malicious payloads, and when unsuspecting users click these links, the script executes in their browsers.

Here's a vulnerable ASP.NET Core

A code snippet that reflects user input without proper encoding:

[HttpGet]
public IActionResult GreetUser(string name)
{
    ViewData["Greeting"] = $"Hello, {name}!";
    return View();
}
 

In this code, the name parameter is directly included in the HTML response without encoding, making it vulnerable to reflected XSS attacks.

Prevention: Output Encoding

To prevent reflected XSS, always encode user-generated content before rendering it in HTML responses. In ASP.NET Core, you can use the HtmlEncoder class:

[HttpGet]
public IActionResult GreetUser(string name)
{
    ViewData["Greeting"] = $"Hello, {HtmlEncoder.Default.Encode(name)}!";
    return View();
}
 

Encoding user inputs ensures that any injected scripts are treated as plain text, thwarting XSS attacks.

Stored XSS

Stored XSS occurs when an attacker stores a malicious script on a server, which is later served to other users who view the compromised page. It's hazardous as it can affect multiple users.

Here's a vulnerable code snippet that stores and serves user input without proper encoding:

[HttpPost]
public IActionResult PostComment(string comment)
{
    // Vulnerable code
    _commentService.SaveComment(comment);
    return RedirectToAction("Index");
}
 

In this code, user comments are stored without encoding, making it possible for attackers to inject malicious scripts into comments.

Prevention: Input Validation and Output Encoding

To prevent stored XSS, validate and sanitize user-generated content and always encode it when rendering in HTML responses. Here's a secure version of the code:

[HttpPost]
public IActionResult PostComment(string comment)
{
    // Secure code
    var sanitizedComment = HtmlEncoder.Default.Encode(comment);
    _commentService.SaveComment(sanitizedComment);
    return RedirectToAction("Index");
}
 

By validating, sanitizing, and encoding user inputs, you can protect your application against stored XSS attacks.

Conclusion

In exploring common security vulnerabilities, we've covered many injection attacks, from SQL injection to cross-site scripting (XSS). Understanding these vulnerabilities and implementing best practices for prevention is crucial in ensuring the security of your ASP.NET Core applications.

Security is an ongoing process; staying informed about the latest threats and mitigation techniques is essential. By following secure coding practices, validating and sanitizing user inputs, and employing proper output encoding, you can significantly reduce the risk of falling victim to injection attacks and other security vulnerabilities. Stay vigilant, and keep your applications safe and secure.

Exploring Common Security Vulnerabilities: A Comprehensive Guide

Introduction:

Security vulnerabilities are software or hardware systems weaknesses that malicious actors can exploit to gain unauthorized access to sensitive data or compromise a system's integrity. This article will delve into common security vulnerabilities and learn how to safeguard our applications against threats. We will start by exploring injection attacks and then move on to other prevalent vulnerabilities, emphasizing the importance of awareness and proactive prevention.

Understanding Injection Attacks:

Injection attacks represent a significant threat in the cybersecurity landscape. They encompass various attack types, including SQL, command, CRLF, and LDAP injections.

  •  SQL Injection: This attack involves injecting malicious SQL code into an application to gain unauthorized access to sensitive data stored in a database. It can have severe consequences for data security.
  •  Command Injection: Malevolent users inject command-line commands into a vulnerable application, aiming to execute them within the operating system. This attack can lead to unauthorized system access or system damage.
  •  CRLF Injection: By injecting special characters into an HTTP request, attackers manipulate the request, causing damage to the server.
  •  LDAP Injection: Malicious LDAP statements are injected into an application to manipulate the LDAP directory.

In the following sections, we will delve deeper into each of these injection types and explore strategies for their prevention.

File Upload Attacks:

File upload attacks involve malicious files uploaded to a system, potentially compromising security. For instance, imagine a scenario where a user is asked to submit their CV via a web form, but instead of a legitimate document, a malicious file is uploaded.

Authentication Attacks:

Authentication attacks, often called brute force or guessing attacks, occur when an attacker repeatedly attempts to guess a user's password by submitting numerous authentication requests. A standard preventive measure is to limit the number of wrong attempts a user can make, locking the account after a specific threshold, like five failed login attempts.

Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF/XSRF):

Cross-site scripting (XSS) entails attackers injecting client-side code into web applications through input or text areas. In contrast, Cross-Site Request Forgery (CSRF/XSRF) involves using a user's authenticated session to send unauthorized requests. Both of these vulnerabilities can lead to various security breaches.

The Same-Origin Policy and Cross-Origin Resource Sharing (CORS):

Attackers can leverage third-party application tools to access your application, violating the same-origin policy and potentially compromising the security of your system. Understanding and implementing CORS effectively is crucial to mitigate this threat.

Conclusion:

This article overviews the top five common vulnerability attacks that can pose significant risks to your applications and systems. In subsequent sections, we will explore these vulnerabilities in greater detail, offering insights into prevention strategies and best practices for enhancing cybersecurity. Stay tuned for a deep dive into the world of security.

Unblocking Angular CLI: Resolving the PS1 Error in PowerShell

Introduction

When working with Angular CLI (Command Line Interface) and PowerShell on Windows, you may encounter an error message that says, “PS1 cannot be loaded because running scripts is disabled on this system.” This error typically occurs due to the security settings on your system that prevent the execution of PowerShell scripts. However, you must continue using Angular CLI effectively. This guide will walk you through the steps to fix this error and get back to your Angular development workflow.

Understanding the Error

Your error message is related to PowerShell’s script execution policy. PowerShell has different execution policies determining whether scripts can be run and under what conditions. The default execution policy on many Windows systems is often set to “Restricted,” which prevents the execution of scripts, including Angular CLI scripts.

PS E:\DevWorkspaces\GitHub\niranjankala\ms-learn\src\dotnet-core\Summaries> ng -v
ng : File C:\Users\niran\AppData\Roaming\npm\ng.ps1 cannot be loaded. The file C:\Users\niran\AppData\Roaming\npm\ng.ps1 is not digitally signed. You cannot run this script on the current system. For 
more information about running scripts and setting execution policy, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:1
+ ng -v
+ ~~
    + CategoryInfo          : SecurityError: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess

How To Fix Error “PS1 Can Not Be Loaded Because Running Scripts Is Disabled On This System” In Angular

When working with Angular CLI (Command Line Interface) and PowerShell on Windows, you may encounter an error message that says, “PS1 cannot be loaded because running scripts is disabled on this system.” This error typically occurs due to the security settings on your system that prevent the execution of PowerShell scripts. However, resolving this issue is essential to continue using Angular CLI effectively. This guide will walk you through the steps to fix this error and get back to your Angular development workflow.

Understanding the Error

Your error message is related to PowerShell’s script execution policy. PowerShell has different execution policies determining whether scripts can be run and under what conditions. The default execution policy on many Windows systems is often set to “Restricted,” which prevents the execution of scripts, including Angular CLI scripts.

Solution: Change the Execution Policy

To resolve this issue, you must change the PowerShell execution policy to allow script execution. Here are the steps to do that:

  1. Open PowerShell as an Administrator:
    • Click on the Windows Start button.
    • Search for “PowerShell.”
    • Right-click on “Windows PowerShell” or “PowerShell” (depending on your Windows version) and select “Run as administrator.”
  2. Check the Current Execution Policy:
    • In the PowerShell window, type the following command and press Enter:
      Get-ExecutionPolicy
      
    • This command will display the current execution policy. If it’s set to “Restricted,” you need to change it.
  3. Change the Execution Policy:
    • To change the execution policy, you can use the following command:
      Set-ExecutionPolicy RemoteSigned
      

      The “RemoteSigned” policy allows the execution of locally created scripts that require a digital signature for scripts downloaded from the internet.

    • You might be prompted to confirm the change. Type “Y” for Yes and press Enter.
  4. Verify the New Execution Policy:
    • To ensure that the execution policy has been changed successfully, run the following command:
      Get-ExecutionPolicy
      

      It should now display “RemoteSigned.”

  5. Restart PowerShell:
    • Close the PowerShell window and open a new one for the changes to take effect.
  6. Run Your Angular CLI Command:
    • Now that you’ve allowed script execution, you should be able to run Angular CLI commands without encountering the “PS1 cannot be loaded” error.

Caution

Changing the execution policy to “RemoteSigned” makes your system more permissive with regard to script execution. While this change is necessary for running Angular CLI commands, it’s essential to be cautious when running scripts from untrusted sources. Ensure you run scripts from reliable and trusted locations to minimize security risks.

Conclusion

By changing the PowerShell execution policy to “RemoteSigned,” you can resolve the “PS1 cannot be loaded” error and continue using Angular CLI without issues. However, remember to exercise caution when executing scripts and only run scripts from trusted sources to maintain the security of your system. With this error resolved, you can smoothly work on your Angular projects using PowerShell.

How to Fix "ORA-01489: Result of String Concatenation is Too Long" with LISTAGG Function

Introduction

Oracle Database is a powerful relational database management system offering developers and database administrators various functionalities. One of these powerful features is the LISTAGG function, which allows you to aggregate rows of data into a single concatenated string. However, when using the LISTAGG function with large datasets, you may encounter an error message like "ORA-01489: result of string concatenation is too long." In this article, we will explore the causes of this error and discuss various methods to prevent and handle it effectively.

Understanding the ORA-01489 Error

The ORA-01489 error occurs when the result of concatenating strings with the LISTAGG function exceeds the maximum allowed length for a string in Oracle, which is 4000 characters for non-CLOB data types in most environments. When the concatenated string surpasses this limit, the error is triggered, and the query fails to execute.

Solutions to Prevent ORA-01489 Error

  1. Truncate the Result with ON OVERFLOW TRUNCATE:
    Starting from Oracle 19c, you can use the ON OVERFLOW TRUNCATE clause with the LISTAGG function. This option truncates the concatenated string to fit within the maximum string length allowed.
    SELECT 
        LISTAGG(column, delimiter) WITHIN GROUP (ORDER BY column) ON OVERFLOW TRUNCATE AS concatenated_result
    FROM 
        table_name;sq

    Please be careful when using this approach since it may lead to data loss if the truncated portion contains essential information.
  2. Filter Data to Reduce Concatenation Size:
    If the error occurs due to a large number of rows being concatenated, consider filtering the data before using the LISTAGG function. Reducing the number of rows in the result set can help keep the concatenated string within the allowed length.
    SELECT 
        TO_CLOB(LISTAGG(column, delimiter) WITHIN GROUP (ORDER BY column)) AS concatenated_clob
    FROM 
        table_name;


  3. Use CLOB Data Type for Concatenation:
    If the concatenated result is expected to exceed the maximum allowed length for a non-CLOB data type, you can use the CLOB data type to store the result. CLOB can handle much larger strings, making it suitable for situations requiring extensive concatenation.
    SELECT 
        TO_CLOB(LISTAGG(column, delimiter) WITHIN GROUP (ORDER BY column)) AS concatenated_clob
    FROM 
        table_name;
    Please note that CLOB data type has its limitations, and you should ensure it aligns with your database configuration.
  4. Partition of the Data:
    If possible, partition the data and perform concatenation on smaller subsets. You can then combine the results of each subset to get the final concatenated string.
    SELECT 
        RTRIM(
            XMLAGG(XMLELEMENT(e, column || delimiter) ORDER BY column).EXTRACT('//text()'),
            delimiter
        ) AS concatenated_result
    FROM 
        table_name;
  5. Use XMLAGG and LISTAGG Combination:
    Another approach to overcome the ORA-01489 error is using XMLAGG in combination with LISTAGG. The XMLAGG function helps avoid the string length limitation by aggregating data into XML elements before concatenation.
    SELECT 
        RTRIM(
            XMLAGG(XMLELEMENT(e, column || delimiter) ORDER BY column).EXTRACT('//text()'),
            delimiter
        ) AS concatenated_result
    FROM 
        table_name;
  6. Limit the Concatenation Length:
    If you don't want to truncate the result or use CLOB, you can limit the maximum length of the concatenated string to a specific value using the SUBSTR function.
    SELECT 
        SUBSTR(LISTAGG(column, delimiter) WITHIN GROUP (ORDER BY column), 1, max_length) AS concatenated_result
    FROM 
        table_name;
    In this example, max_length should be the maximum allowed length for the concatenated string.

Conclusion

The LISTAGG function in Oracle Database is valuable for aggregating data into a single concatenated string. However, if the result exceeds the maximum string length allowed, the ORA-01489 error may occur when dealing with large datasets. Applying one or a combination of the solutions provided in this article can effectively handle and prevent the ORA-01489 error, allowing you to manage and manipulate large datasets without encountering string concatenation length limitations. You can choose the solution that best fits your specific use case and database configuration to ensure efficient and error-free data aggregation.

How to download and install PostgreSQL?

Introduction

This article will teach you how to download and install PostgreSQL on your system.

Downloading and Installing PostgreSQL

To get started working with Postgres, you need two components.

  1. Postgres Server
    It houses your data 
    and manages connections, security, and data maintenance. 

  2. Management Application
    You need a way to communicate with a server 
    to work with and view the data it contains. For this, you'll need a management application or what is sometimes referred to as a front-end client. There are lots of different clients that you can use to interface with a Postgres Server. 

 

The easiest way to get started is to use an installer that gives us both components in one step. Follow the below step to download and install PostgreSQL:

Step 1

You'll start the installation at the URL postgressql.orgIt is the official site for Postgres, and you will find a big download button on the top and click on it.

 

 Step 2

Now a new page will open, with several different application installers available depending on what operating system you're working on.

If you're working on a Linux machine, you will likely already have a version of Postgres since it comes pre-installed with the OS. 

The instructions you'll find at these links will help you install an alternate version if you need to. The installation for your macOS and Windows is almost identical. I'm working on a Windows computer, so I will click on the Windows button highlighted with the yellow color. 

Step 3

That takes me to a page that describes what versions of Postgres are available on each platform, either a 64-bit or 32-bit. 

 

After viewing the platform support information, you can click on the link up here that says download the installer. It takes you to a page maintained by a company called EnterpriseDB. 

Step 4

Finally, here you can choose which version of Postgres you want. 

 

I'll select the downloader for your Postgres by clicking on the download link underneath Windows. 

Step 5

It will start the installation package download, and once the download is completed, as I can see my screen's download progress, you can close the web browser and start the installation process. 

 

You should find the file inside the downloads folder for your computer. Go ahead and double-click on it to start it up. I'll allow it to make changes to my computer. 

Step 6

So that starts the setup wizard. Let's press the next button on this screen. 

 Step 7

The default installation directory will be on my computer, inside the program files folder, a new folder for PostgreSQL, and then the version. I'll leave it to default and press next. 

 Step 8

Here, you have four different components that you can install. 

The PostgreSQL Server is the main component, so I want to make sure that that's turned on. 

You have two interface clients that are coming along, pgAdmin 4 is a graphical user interface, and I'll leave that on, as well as the command line tools, and I'll leave those on as well. 

Stack Builder is a tool that will help you install additional add-on packages if you're interested in extending Postgres' capabilities. You can turn this off if you'd like. I'll leave it on here to install everything, and I'll go ahead and press the next button. 

Step 9

Next, it sets up a default data directory. And once again, on my computer, as in program files, PostgreSQL version, and then a new folder called data.

 I'll choose that default and press next. 

Step 10

You'll need to have a user account to connect to the server. Here, the installer is creating a default user that's named Postgres. And you need to create a password for this user. 

You can type in whatever password you'd like. Just make sure that you remember it moving forward. This user account is going to be an administrator-level account. So this user will have permission to do anything on the server.

Using this, you can create additional user accounts to control access to the stored data in Postgres. Once you've typed in the password and retyped the same password, press the next button. 

Step 11

The default port that Postgres will communicate on is 5432. 

You'll just leave that there and press next. 

Step 12

On this screen, you get to choose a default location. Once again, I'll leave this as the default locale and press next. 

Step 13

Now the installer will give me an installation summary. 

I'll press the next button and the next one more time to install the software. 


Step 14

When that finishes, the database server and client applications have been installed. 

Now, if you can also choose to install the Stack Builder application, you can turn off this checkbox here. You don't need to run Stack Builder when you finish out of this wizard, and I'll uncheck this checkbox and press the finish button. 

 

All done. It is time to test the installation, and you will find the management application to connect with the server.

Now you can go into my start menu, and I'm going to scroll down and find the new folder that was just added for PostgreSQL <version>. Inside of here, you have a couple of applications.

 

 

 I am only interested in below two applications:

  1. pgAdmin 4 - This is a graphical user interface for Postgres that'll run inside your web browser. 
  2. SQL Shell or PSQL - This is a command line tool popular with programmers. 

    You'll find the PostgreSQL <version> folder on macOS inside your applications folder. And inside, there will be the same pgAdmin and PSQL applications. So now you have Postgres Server and a couple of client applications for us to work with.

 Open the SQL Shell, a command-line window, and start the login process.

You need to know some connection details to log into a Postgres server.

Server IP:

First, it asks for the server location. If you're in a typical office environment where the Postgres server is running on a centralized computer, then you'll need the IP address of that machine. In our case, we're running the server and client on the same physical machine. So we can use the word localhost instead.

You can either type that in or press Enter, and localhost, the default indicated by the text in square brackets, will be used instead. I'll leave this blank and press Enter to enter in localhost.

Database:

Next, it asks you which database you want to connect to. Each Postgres server can hold many different databases. If you have just installed the server, it is brand new, and there's just one database called Postgres, so I'll log into that one. Again, you can press Enter to accept the default value of Postgres.

Communication port:

Next, we need the communication port that the server is listening on. This was set up during the installation step but is typically left at the default of 5432. I'll press Enter to accept that value.

User Name:

Then we need to provide the user account credentials. Again, during the installation, we created a superuser account named Postgres. So we'll use that. You will supply that username here if you've been assigned your own personal user account for your server.

Password:

Finally, you have to enter the user's password. Again, we gave the Postgres user account a password during setup. So I hope that you remember what you filled in during that step. Go ahead and type it in now. When you type, it will not appear on the screen, so just type it out and press Enter when you're done.

If everything was filled in correctly, you should be connected to the Postgres server, and the command prompt will change. Now we can start sending commands to the server.

Conclusion

We have successfully installed PostgreSQL with these simple steps.

 

 

How to Create a JavaScript Library/Framework

Introduction

This article will teach you how to Create a Javascript Library or Framework.

Creating a JavaScript library

Library Name: Greeter

  • When given a first name, last name, and options language, it generates formal and informal greetings.
  • Supports English and Spanish languages.
  • Reusable library/framework.
  • Easy to type ‘G$()’ structure. -Support jQuery
Structure Safe code

HTML

<html>
  <head>      
  </head>
  <body>
      <script src="scripts/jquery-3.6.0.js"></script>
      <script src="scripts/greetr.js"></script>
      <script src="scripts/app.js"></script>
    </body>
</html>

Include jQuery first to enable the jQuery support.

greetr.js

We require a global variable window and jQuery. Set up an IIFE function by passing the windows and the jQuery function.

Now create an IIFE function to start with.


(function(global, $) {
}(window, jQuery));

Now, this is safe to use in any of the applications and ready to use.

The next step is to set up the greeter object and the alias similar to jQuery $. You can review code the code of the jQuery library understands the safe entry method to work with any library.


(function (global, $) {

  var Greetr = function (firstname, lastName, language) {
    return new Greetr.init(firstName, lastName, language);
  };

  // You can create your properties and function here
  Greetr.prototype = {};

  Greetr.init = function (firstName, lastName, language) {
    var self = this;
    self.firstName = firstName || "";
    self.lastName = lastName || "";
    self.language = language || "en";
  };

  Greetr.init.prototype = Greetr.prototype;

  //Set the alias
  global.Greetr = global.G$ = Greetr;
}(window, jQuery));


Adding Language support

Now we set up the language support for English and Spanish. Along with this

(function (global, $) {
  var Greetr = function (firstname, lastName, language) {
    return new Greetr.init(firstName, lastName, language);
  };

  var supportedLanguages = ["en", "es"];

  var greetings = {
    en: "Hello",
    es: "Hola",
  };

  var formalGreetings = {
    en: "Greetings",
    es: "Saludos",
  };

  var logMessages = {
    en: "Logged In",
    es: "iniciar la sesión",
  };

  // You can create your properties and function here
  Greetr.prototype = {
    fullName: function () {
      return this.firstName + " " + this.lastName;
    },
    validate: function () {
      if (supportedLanguages.indexOf(this.language) === -1) {
        throw "Invalid language";
      }
    },
    greeting: function () {
      return greetings[this.language] + " " + this.firstName + "!";
    },

    formalGreetings: function () {
      return formalGreetings[this.language] + " " + this.fullName() + "!";
    },
    greet: function (formal) {
      var msg;
      //if undefined or null, it will be coerced to 'false.'
      if (formal) {
        msg = this.formalGreetings();
      } else {
        msg = this.greeting();
      }

      if (console) {
        console.log(msg);
      }

      //'this' refers to the calling object at the execution time
      // makes the method chainable
      return this;
    },
    log: function () {
      if (console) {
        console.log(logMessages[this.language] + ": " + this.fullName());
      }
      return this;
    },
    setLanguage: function (lang) {
      this.language = lang;
      this.validate();
      return this;
    },
  };

  Greetr.init = function (firstName, lastName, language) {
    var self = this;
    self.firstName = firstName || "";
    self.lastName = lastName || "";
    self.language = language || "en";
  };

  Greetr.init.prototype = Greetr.prototype;

  //Set the alias
  global.Greetr = global.G$ = Greetr;
}(window, jQuery));


Calling the library in the application

var g = G$("Niranjan", "Singh");
//Chained behavior and call to display greetings
g.greet().greet(true);
//Change the language and then greet
g.greet().setLang('es').greet(true);


Adding jQuery support

We need to add jQuery support and provide the functionality to give the id to Greetr library for updating the element text.

Update the HTML page with the below text to enable/demonstrate the jQuery incorporation.


<html>

<head>
</head>

<body>
  <div id="logindiv">
    <select id="lang" div>
      <option value="en">English</option>
      <option value="es">Spanish</option>
    </select>
    <input type="button" name="login" id="login" value="Login">
  </div>
  <h1 id="greeting"></h1>
  <script src="scripts/jquery-3.6.0.js"></script>
  <script src="scripts/greetr.js"></script>
  <script src="scripts/app.js"></script>
</body>

</html>

It requires changes in the Greetr library also. So add a new method called HTMLGreeting with a selector parameter.


    HTMLGreeting: function (selector, formal) {
      if (!$) {
        throw "jQuery not loaded";
      }
      if (!selector) {
        throw "Missing jQuery selector ";
      }

      var msg;
      //if undefined or null, it will be coerced to 'false.'
      if (formal) {
        msg = this.formalGreetings();
      } else {
        msg = this.greeting();
      }

      $(selector).html(msg);

      return this;
    },

Below is the simple library/framework which we have developed. It could be referred to and used to create a library.

(function (global, $) {
  // 'new' an object
  var Greetr = function (firstName, lastName, language) {
    return new Greetr.init(firstName, lastName, language);
  };
  // hidden within the scope of the IIFE and never directly accessible
  var supportedLanguages = ["en", "es"];
  // informal greetings
  var greetings = {
    en: "Hello",
    es: "Hola",
  };
  // formal greetings
  var formalGreetings = {
    en: "Greetings",
    es: "Saludos",
  };
  // logger messages
  var logMessages = {
    en: "Logged In",
    es: "iniciar la sesión",
  };

  // You can create your properties and function here
  Greetr.prototype = {
    fullName: function () {
      return this.firstName + " " + this.lastName;
    },
    validate: function () {
      if (supportedLanguages.indexOf(this.language) === -1) {
        throw "Invalid language";
      }
    },
    greeting: function () {
      return greetings[this.language] + " " + this.firstName + "!";
    },

    formalGreetings: function () {
      return formalGreetings[this.language] + " " + this.fullName() + "!";
    },
    greet: function (formal) {
      var msg;
      //if undefined or null, it will be coerced to 'false.'
      if (formal) {
        msg = this.formalGreetings();
      } else {
        msg = this.greeting();
      }

      if (console) {
        console.log(msg);
      }

      //'this' refers to the calling object at the execution time
      // makes the method chainable
      return this;
    },
    log: function () {
      if (console) {
        console.log(logMessages[this.language] + ": " + this.fullName());
      }
      return this;
    },
    setLanguage: function (lang) {
      this.language = lang;
      this.validate();
      return this;
    },
    HTMLGreeting: function (selector, formal) {
      if (!$) {
        throw "jQuery not loaded";
      }
      if (!selector) {
        throw "Missing jQuery selector ";
      }

      var msg;
      //if undefined or null it will be coerced to 'false'
      if (formal) {
        msg = this.formalGreetings();
      } else {
        msg = this.greeting();
      }

      $(selector).html(msg);

      return this;
    },
  };
  // the actual object is created here, allowing us to 'new' an object without calling 'new'
  Greetr.init = function (firstName, lastName, language) {
    var self = this;
    self.firstName = firstName || "";
    self.lastName = lastName || "";
    self.language = language || "en";
    self.validate();
  };
  // trick borrowed from jQuery so we don't have to use the 'new' keyword
  Greetr.init.prototype = Greetr.prototype;

  //Set the alias, attach the Greetr to the global object and provide a shorthand '$G' for the ease our poor fingers
  global.Greetr = global.G$ = Greetr;
}(window, jQuery));

Conclusion

We have created a small library that supports the jQuery framework also. We can create an extensive library or framework by following the same pattern. The best way to learn this is by reviewing the existing open source libraries and frameworks, e.g., jQuery.

How to resolve Global Exception Logger with Dependency Injection in ASP.NET Web API?

Introduction

In this article, you will learn how to resolve Global Exception Logger with dependency injection in ASP.NET Web API using Autofac.

How to resolve Global Exception Logger with dependency injection

Step 1: Create the custom exception logger by inheriting the IExceptionLogger interface to write your custom logging logic.

/// 
/// The main class GlobalExceptionLogger.
/// Handles the exception logging requests.
/// 
public class GlobalExceptionLogger : IExceptionLogger
{
    /// 
    /// 
    /// 
    public ILogger Logger { get; set; }
    /// 
    /// Initializes a new instance of the  class.
    /// 
    public GlobalExceptionLogger()
    {
        Logger = NullLogger.Instance;
    }
    /// 
    /// Logs the exception.
    /// 
    ///The exception context.
    ///
    /// 
    public async Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken)
    {
        var ex = context.Exception;
        string message = $"{ex.Message}--{ex.Source}\n{ex.StackTrace}\n{ex.TargetSite}\n";
        await Task.Run(() =>
        {
            Logger.Error(ex, message);
        });
    }

}

Step 2: Register your custom exception logger class in the Autofac container to resolve it through dependency injection.

/// 
/// The main class AutofacConfig.
/// Provides Autofac DI configuration of the API.
/// 
public static class AutofacConfig
{

    #region Autofac Container
    private static Lazy builder =
      new Lazy(() =>
      {
          var autofacbuilder = new ContainerBuilder();
          RegisterTypes(autofacbuilder);
          return autofacbuilder.Build();
      });

    /// 
    /// Configured Autofac Container.
    /// 
    public static IContainer Container => builder.Value;
    #endregion

    /// 
    /// Registers the type mappings with the autofac container builder.
    /// 
    ///The autofac container builder to configure.
    /// 
    /// 
    public static void RegisterTypes(ContainerBuilder builder)
    {
       string baseDirectoryPath = AppDomain.CurrentDomain.BaseDirectory + "bin";
        if (!Directory.Exists(baseDirectoryPath))
            baseDirectoryPath = AppDomain.CurrentDomain.BaseDirectory;

        builder.RegisterModule(new LoggingModule());
        builder.RegisterModule(new FileStoreModule());
        //builder.RegisterModule(new CloudJobManager.CloudJobManagerModule());
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
        //builder.RegisterType().InstancePerLifetimeScope();

        var assemblies = Directory.EnumerateFiles(baseDirectoryPath, "*.dll", SearchOption.TopDirectoryOnly)
            .Where(filePath => Path.GetFileName(filePath).StartsWith("MyApp"))
            .Select(Assembly.LoadFrom).Where(assemblyType =>
            (assemblyType.FullName.StartsWith("MyApp") && !assemblyType.FullName.Contains("MyApp.Framework") &&
            !assemblyType.FullName.Contains("MyApp.Reporting.API")
            )).ToArray();

        builder.RegisterAssemblyTypes(assemblies)
        .AsImplementedInterfaces().InstancePerLifetimeScope();

        builder.RegisterType().AsSelf().AsImplementedInterfaces();
        builder.RegisterType().AsSelf().AsImplementedInterfaces();

        builder.RegisterType<ReportService>().As<IReportService>().InstancePerRequest();

    }
}

Step 3: Replace the custom exception logger in the HttpConfiguration services to use it in the place of the default ASP.NET exception logger.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        config.DependencyResolver = new AutofacWebApiDependencyResolver(AutofacConfig.Container);

        // Web API configuration and services
        //config.Services.Replace(typeof(IExceptionLogger), new  GlobalExceptionLogger());
        //config.Services.Replace(typeof(IExceptionHandler), new GenericExceptionHandler());

        // Inject our exception logger and handler
        config.Services.Replace(typeof(IExceptionHandler), config.DependencyResolver.GetService(typeof(GenericExceptionHandler)));
        config.Services.Replace(typeof(IExceptionLogger), config.DependencyResolver.GetService(typeof(GlobalExceptionLogger)));

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}
Conclusion

Whenever an unhandled error occurs then you have a chance to log it. The information regarding the can be stored somewhere for review. There you can write the issue to a log or write custom logic.

Azure DevOps Server - How to fix indexing isn't working issue?

Introduction

In this article, you will learn how to fix the Azure DevOps Server indexing issues.

How to fix indexing isn't working, or is in progress issue

In our scenario, the Search was not working and was completely broken. Nobody was able to search in the code and work items.

To fix this, we have referenced the Microsoft documentation - Manage Search indexing to create the search index again.

Below is the step to fix the search indexing:

  • Download the scripts from the Code-Search GitHub repository on the server.
  • Extract the zip somewhere and open the Powershell in Administrative mode.
  • Change the directory to your Azure DevOps Server version. I have to reindex the entire collection.
  • Now execute the script TriggerCollectionIndexing.ps1 to reindex the collection but first, you need to change the policy to execute the command
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
  • After that run the TriggerCollectionIndexing.ps1 file. You need to enter the SQL server instance name of the Azure DevOps Server, Collection database name, Configuration database name, and the entities to reindex.

Conclusion

Search indexing is an important feature in the Azure DevOps Server, use the scripts to get the status of the search indexing and fix the issues.

SQLite- Check If Table Exists

Introduction

In this article, you will learn to check if a table exists in the SQLite database or not.

How to check if a table exists in the database

You execute the below command to check whether a table exists or not in the SQLite database:

SELECT count(*) FROM sqlite_master WHERE type='table' AND name='tableName';

It will return a value of either 0 or greater than 0. If the table doesn’t exist then the result will be 0 otherwise 1.

You can use this query in any programming language to the existence of the table in the database. For an example below .NET code block will check the existence of the table in the SQLite database:

string sqliteDBFile = string.Format("{0}\\{1}", "D:\\Logs", "Log.db");
string tableName = "LogTable";
string columnName = "CreatedOn";
if (!File.Exists(sqliteLogDBFile))
{
    using (SQLiteConnection con = new SQLiteConnection(string.Format("data source={0}", sqliteLogDBFile)))
    {
       if (CheckIfTableExists(con, Constants.SQLiteLogTableName))
       {
            if (!CheckIfColumnExists(con, tableName, columnName))
            {
                        
            }
        }
    }
}

private bool CheckIfTableExists(SQLiteConnection conn, string tableName)
{
    if (conn.State == System.Data.ConnectionState.Closed)
        conn.Open();

    using (SQLiteCommand cmd = new SQLiteCommand(conn))
    {
        cmd.CommandText = $"SELECT count(*) FROM sqlite_master WHERE type='table' AND name='{tableName}';";
        object result = cmd.ExecuteScalar();
        int resultCount = Convert.ToInt32(result);
        if (resultCount > 0)
            return true;

    }
    return false;
}

You can also check that that if a column exists in the table or not using the below line of code which uses the table metadata and then check the column name for the match.

private bool CheckIfColumnExists(SQLiteConnection conn, string tableName, string columnName)
{
    if (conn.State == System.Data.ConnectionState.Closed)
        conn.Open();

    using (SQLiteCommand cmd = new SQLiteCommand(conn))
    {
        cmd.CommandText = string.Format("PRAGMA table_info({0})", tableName);

        var reader = cmd.ExecuteReader();
        int nameIndex = reader.GetOrdinal("Name");
        while (reader.Read())
        {
            if (reader.GetString(nameIndex).Equals(columnName))
            {
                return true;
            }
        }
    }
    return false;
}

Checking a table exists or not before creating a new table

Checking a table before creating or dropping a table is the common use case. To run the query without failing it with a fail-safe check. In SQLite, you can be sure that a table should be created in the database if it does not exist using the below query:

CREATE TABLE IF NOT EXISTS <table_name> (column1_name <datatype>,....)

Conclusion

You have learned how can check the existence of the database base objects in an SQLite database.

NDepend - Dependency Graph Navigating - Coupling Graph

Introduction

This is another post followed by NDepend - Improved Dependency Graph Feature. In this article, you will discover more about the Dependency Graph features e.g. focus on a node (double click a node in graph) or coupling graph (double click an edge in the graph).

Again, we going to use the same OrchardCore project used in the previous article on NDepend Dependency Graph. Open the project and then navigate to the Dependency Graph Zoom in a bit either using the mouse wheel or button in the toolbar. 

Focusing on a node

Select a node in the graph and double click on it. Now you will see that node will be in the center of the diagram and it is in the view of focus. Now you can see the element and paths of the selected node.

Transitioning to coupling graph

Coupling is related to the dependency between the modules. You can see the Dependency Graph by double click an edge in the graph. It is a great feature to go through the different dependencies path for the module node, you selected.

Here is the visualization of the navigation in the Dependency Graph

Conclusion

NDepend Dependency Graph has a smooth transition between the different dependencies and it is good that it is navigating to the coupling graph just by clicking on the edges.