Giter Club home page Giter Club logo

csaf-validator-lib's Introduction

BSI Secvisogram CSAF 2.0 Web Editor

About The Project

Secvisogram is a tool for creating and editing advisories in CSAF format.

Secvisogram is inspired by the project Vulnogram - "a tool for creating and editing CVE information in CVE JSON format". Both names share the same Greek suffix '-gram' which is used for denoting something written or recorded in a particular way. Vulnerability-related information is often not enough - mostly, only the remediation information enables the end user to act efficiently in responding to these concerns. This information is usually conveyed in Security Advisories. Therefore, the first part of the name Secvisogram abbreviates the words Security Advisory.

Secvisogram aims to make it easier for vendors and other security advisory issuing parties to record the advisory details in the CSAF format.

Secvisogram CSAF Editor Screenshot

(back to top)

Getting started

Assure that you have Node 20 (LTS) and npm 9 or newer installed. Nodesource provides binary distributions for various Linux distributions.

  $ node --version ; npm --version
  v20.9.0
  9.9.2

Check out the repository and navigate to the working directory.

git clone [email protected]:secvisogram/secvisogram.git
cd secvisogram

Afterwards, the npm dependencies need to be installed.

npm ci

Now you can start a development server as follows:

npm run dev

The application is now running and accessible at http://localhost:8080. Use the --port argument for alternative ports, e.g. npm run dev --port=8081.

You can configure the application by providing a json config under the following path .well-known/appspecific/de.bsi.secvisogram.json. During development this file has to be in app/public/.well-known/appspecific. This is only needed when you are using secvisogram in combination with the CSAF CMS Server. If no config is available Secvisogram will fall back to the standalone mode which is the same as "loginAvailable": false.

{
  "loginAvailable": true,
  "loginUrl": "/oauth2/sign_in?rd=http%3A%2F%2Flocalhost%3A8080",
  "logoutUrl": "/oauth2/sign_out?rd=http%3A%2F%2Flocalhost%3A8080",
  "userInfoUrl": "/oauth2/userinfo",
  "validatorUrl": "http://localhost:8082"
}

(back to top)

Deploying to Production

Please refer to DEVELOPMENT.md for a detailed description on how to build and deploy Secvisogram in production.

Configure keybindings

The following hotkeys are available by default:

shortcut effect comment
CTRL + O New / Open Opens dialog
CTRL + S Save Opens dialog
CTRL + ALT + V Validate
CTRL + ALT + E Open export dialog
CTRL + E Open export dialog CSAF json (stripped) preselected
CTRL + SHIFT + E Open export dialog CSAF json preselected
CTRL + P Open export dialog HTML preselected
CTRL + ALT + P Open export dialog PDF preselected
CTRL + 1 Switch to FormEditor tab
CTRL + 2 Switch to JsonEditor tab
CTRL + 3 Switch to Preview tab
CTRL + 4 Switch to CSAF Document tab
CTRL + SHIFT + 1 Select relevance level "mandatory" Only in FormEditor tab
CTRL + SHIFT + 2 Select relevance level "best practice" Only in FormEditor tab
CTRL + SHIFT + 3 Select relevance level "want to have" Only in FormEditor tab
CTRL + SHIFT + 4 Select relevance level "nice to have" Only in FormEditor tab
CTRL + SHIFT + 5 Select relevance level "optional" Only in FormEditor tab
f8 Jump to next error f8 is the fixed key in the JsonEditor

These can be overwritten if needed by providing an alternative keybinding in .well-known/appspecific/de.bsi.secvisogram.json. Possible keynames are defined in: react-hotkeys

{
  ...,
  "keyBindings": {
    "keyNew": "ctrl+o",
    "keySave": "ctrl+s"
  }
}

The JSON Editor View has additional keybindings available. To get an overview please press F1. Hint: The list is scroll and searchable.

How to use

Basic concepts

Similar to the Vulnogram model, the web application consists of various tabs that represent the individual views. These are represented as an HTML page which loads its data from a local browser storage using JavaScript.

To start a new document after saving, click the "New" button. Alternatively, click the "Open" button to select and load a local file.

When leaving a view without saving, e.g. reloading the application, a warning is displayed, and the user must confirm prior to leaving the page.

The document/tracking/engine data of new documents will be prefilled with name and version of Secvisogram. When running with the CSAF CMS Server, the backend will manage these fields along with the tracking id, and revision history.

Some fields allow the usage of markdown to format text. Further information can be found in the Markdown Guide

(back to top)

Form Editor

The Form Editor view is an HTML form with live input validation. Additionally, the CSAF JSON Schema checks the following constraints:

  • Language fields in the form are checked for plausibility against the values from the IANA database
  • The consistency of CVSS string and accompanying fields is checked
  • The consistency of the CWE ID and description is checked

CVSS 3 input fields are completed with the data from a possibly copied vector string and their values are recalculated. This gives the user an elegant way to use a possibly existing and copied CVSS 2.0 vector and to partially adjust the values.

A special input field is the "CWE" attribute. Here you can search the CWE catalog (XML file) by entering a value in the "id" or "name" field. For this purpose, a list with the first ten entries matching the input opens under the respective input field, from which a suitable entry can be selected and accepted.

Input errors are displayed directly alongside the respective form field. Issue counters and symbols on the top right indicate the current validation status. Clicking on this indicator reveals the sidebar with all validation issues and allows to inspect and directly jump to the respective fields.

Opening & Saving Files You can open and save your CSAF JSON document at any time using the respective Open and Save buttons. If your document fails the validation checks, a confirmation dialog will appear.

The CSAF Document tab offers functionality to extract the standard-valid subset of your current document.

When running with the CSAF CMS Server, saving documents on the server and loading documents from the server is also possible for logged-in users. These users will see the corresponding CSAF Documents Tab on the top right.

Identifying & Solving Validation issues Opening the error view in the sidebar will reveal a linked list of validation issues. Here you can click on any validation issue and directly jump to the affected form elements.

The Form Editor's interface and behavior can be configured by adjusting field metadata. For more detail, see the corresponding readme.

(back to top)

JSON Editor

The JSON Editor view uses the Monaco editor to edit the JSON representation of the current CSAF document. Please note that only valid JSON content is accepted for further processing.

Hovering over a field will display validation issues. You can also click on errors in the sidebar to jump to the affected elements. Setting the conent displayed in the sidebar is also possible by right-clicking on a field and setting the sidebar context.

Note that multiline strings are not supported in JSON value fields and will break this navigation. To add line breaks type \n instead.

(back to top)

Preview (HTML view)

This view does not include any editing functionality. It displays a rendered HTML template view of the current CSAF document as shown in the editor views.

Use the toggle button to switch between the Rendered web view and the HTML source view. You can export this HTML document as a standalone HTML document or rendered PDF by selecting the matching option in the export dialog.

(back to top)

CSAF Document (JSON view)

This view does not include any editing functionality. It always displays the valid subset of your current CSAF document by removing any invalid and/or empty CSAF document elements.

You can use this view and the embedded Export CSAF button to always quickly extract the standard-valid subset of your current CSAF document.

(back to top)

Documentation

The documentation on CSAF documents lives in the secvisogram-documentation repository and is integrated with git-subtree. To update the documentation you can use the following command:

git subtree pull --prefix docs https://github.com/secvisogram/secvisogram-documentation.git main --squash

(back to top)

Contributing

Please refer to CONTRIBUTING.md for details about how to contribute to the development of Secvisogram.

Developer Guide, Architecture and Technical Design

The DEVELOPMENT.md document gives an overall introduction on how to get started with developing Secvisogram as well as an overview on the architecture, libraries used and technical design of Secvisogram.

(back to top)

Custom Preview Templates

It's possible to change and provide custom Preview Templates. Please refer to PREVIEW-TEMPLATING.md for detailed instructions on how to create, modify and deploy Secvisogram with custom templates.

(back to top)

Security Considerations

Please refer to SECURITY-CONSIDERATIONS.md for details about how Secvisogram addresses the OWASP Top 10 Web Application Security Risks.

(back to top)

csaf-validator-lib's People

Contributors

cloeser avatar domachine avatar marcokorinth avatar mfd2007 avatar reneschultheis avatar stefannemeth avatar tschmidtb51 avatar valentinkoe avatar

Stargazers

 avatar

Watchers

 avatar  avatar

csaf-validator-lib's Issues

Publish package

In order to more easily consume this functionality, it would be great if that could be published to npmjs.org

Check CVSSv3.0

The CVSS SIG recently corrected a bug in the pattern part ((M)PR had an additional /non intentional extra U). We need to check whether our code has the same mistake.

Unify order of output

Currently, there are many different ways the json output is ordered in different tools (csaf-validator-lib, csaf-validator-service, csaf_validator).

Interface anpassen

Die Typdefinitionen von Ajv beinhalten, dass es Fehler geben kann, die keine message Property besitzen. In diesem Fall soll die Standardmeldung unexpected empty error message generiert werden.

Add script to test files

We should add a script to test JSON files directly with the lib. It could look similar to the one @domachine provided during #3:

#!/usr/bin/env node

import { readFile } from 'fs/promises'
import * as schemaTests from '../schemaTests.js'
import * as mandatoryTests from '../mandatoryTests.js'
import * as optionalTests from '../optionalTests.js'
import * as informativeTests from '../informativeTests.js'
import validate from '../validate.js'

const [, , filePath, testName] = process.argv

const json = JSON.parse(await readFile(filePath))
console.log(JSON.stringify(json, null, 2))
const tests =
  /** @type {Array<import('../lib/shared/types.js').DocumentTest>} */ (
    Object.values(mandatoryTests)
  )
    .concat(Object.values(optionalTests))
    .concat(Object.values(informativeTests))
    .concat(Object.values(schemaTests))

const matchingTests = tests.filter((t) => t.name === testName)

if (!matchingTests.length)
  throw new Error(`No tests matching "${testName}" found`)
console.log(JSON.stringify(await validate(matchingTests, json), null, 2))

Maybe, we could adopt it, that the output of the JSON is optional...

Test 6.3.8 is missing

I postponed the implementation for a bit since we plan to use hunspell here and the implementation is therefore not "straight forward".

Test 6.3.9

Test 6.3.9 has some bugs: (See https://github.com/oasis-tcs/csaf/pull/583/files for test files)

  • valid files are considered invalid: e.g. oasis_csaf_tc-csaf_2_0-2021-6-3-09-12.json
    {
      "tests": [
    	{
    	  "isValid": true,
    	  "errors": [],
    	  "warnings": [],
    	  "infos": [
    		{
    		  "instancePath": "/product_tree/branches/0/branches/0/branches/0/branches/1",
    		  "message": "missing ancestor with category vendor"
    		}
    	  ],
    	  "name": "informativeTest_6_3_9"
    	}
      ],
      "isValid": true
    }
    
  • invalid files do no show all errors: e.g. oasis_csaf_tc-csaf_2_0-2021-6-3-09-03.json reports only on instead of 4 errors (for 2 elements both defined error messages)
    	{
        "tests": [
      	{
      	  "isValid": true,
      	  "errors": [],
      	  "warnings": [],
      	  "infos": [
      		{
      		  "instancePath": "/product_tree/branches/0/branches/0/branches/0",
      		  "message": "missing ancestor with category vendor"
      		}
      	  ],
      	  "name": "informativeTest_6_3_9"
      	}
        ],
        "isValid": true
      }
    

Dokumentationsanpassungen

Dokumentieren, dass in die validate Funktion nur vertrauenswürdige Funktionen übergeben werden dürfen

Review of Optional and Informative Tests

Here's a list of issues I found reviewing the already checked in optional and informative tests. For tests not mentioned, I could not find any issues.

Optional Tests

  • Test 6.2.1

    • Is the ?? false part necessary for all the checks? If so, I'd like to understand better why. I'm referring to these lines.
  • Test 6.2.10

    • The test checks if the tlp label is present but it does not check for validity of the used label, i.e. if they are one of the allowed enum values AMBER, GREEN, RED, or WHITE.
  • Test 6.2.11

    • The test does not check whether the url starts with https://.
    • It does falsely replace - characters for checking the string.
    • The specification has changed and now replaces sequences of not allowed characters with a single underscore. The new specification also supplies a regex that can be used to the the replacement: [^+\-a-z0-9]+
  • Test 6.2.12

    • Running and modifying the existing test, it seems like skipping the lang property completely will pass the test. If I understand the specification correctly, it should also fail if the property isn't present at all.

Informative Tests

  • Test 6.3.2
    • The schemas for CVSS v3.0 and v3.1 require the properties version, vectorString, baseScore, and baseSeverity while here they are listed as optionalProperties. It's probably out of the scope of this test, but I'm wondering if we could make it conform to the schema more. The same may apply for test 6.3.1.
    • An additional check for the pattern of vectorString could be performed to ensure v3.1 is used. Checking for the version prefix may be fine, though and this would be out of the scope of this test resp. extend it way beyond what is required by the specification.

Test 6.2.13: Check sorting

The CSAF standard requires that all "keys in a CSAF document are sorted alphabetically". It looks like the current implementation also checking elements like /vulnerabilities/product_status[].

This must be corrected to reflect the standard.

Fix test 6.1.24

The current implementation of 6.1.24 evaluates the involvement status over all vulnerabilities. However, the spec intends to do that per vulnerability.

Test 6.1.8 missing

Test 6.1.8 is currently missing. I guess that should be implemented...

Fix code-coverage

Unfortunately the upgrade to Node.js's ES Module package format broke istanbul which is used for test-coverage reporting. An option might be c8 which collects coverage using native v8 instruments.

Expose group of tests

We need to expose the group of tests as specified in the standard:

  • basic: schemaTest (strict) + mandatoryTests (may skip 6.1.8 when covered by strict schema)
  • extended: basic+ optionalTests
  • full: extended + informativeTests

Document submodule dependency

A "normal" git clone followed by npm ci and npm test won't provide the intended result as the submodule has to be imported. Please adopt the documentation in README. (You can use the original one from Secvisogram)

Todos:

  • Copy and adopt the original Readme

Example doesn't work

The README lists the example:

import validate from '../csaf-validator-lib/validate.js'

const document = '{}'
const tests = [
  {
    type: 'preset',
    name: 'mandatory'
  },
  {
    type: 'test',
    name: 'optionalTest_6_2_1'
  }
]

const result = await validate(tests, document)

However, that does not work for me. It throws an error:

file:///anywhere/csaf-validator-lib/lib/validate.js:11
    const result = await test(doc)
                         ^

TypeError: test is not a function
    at default (file:///anywhere/csaf-validator-lib/lib/validate.js:11:26)
    at file:///anywhere/csaf-validator-lib/testing.js:15:22
    at ModuleJob.run (internal/modules/esm/module_job.js:183:25)
    at async Loader.import (internal/modules/esm/loader.js:178:24)
    at async Object.loadESM (internal/process/esm_loader.js:68:5)
    at async handleMainPromise (internal/modules/run_main.js:59:12)

Based on a quick search, I guess the tuple of typeand name isn't implemented here. Correct?

Update Interface documentation

The Interface documentation currently does not list, that the test name is provided as well. We need to check the documentation and update it accordingly.

Add keywords to package.json

We should add keywords to the package.json to improve the search results in the npm registry... At least the following should be added:

csaf
csaf-validator-lib
csaf full validator
secvisogram

Identical datetimes version matching

Validation issue for identical date-times in revisions

Revisions with identical date-times cause the tracking/version to throw an version does not match latest revision error when the tracking->version is the higher/later number form the revision_history:

{
  "document": {
    "csaf_version": "2.0",
    "category": "ACME Security Advisory",
    "title": "ACME Security Advisory",
    "publisher": {
      "name": "ACME",
      "namespace": "a:b",
      "category": "vendor"
    },
    "tracking": {
      "id": "acme-adv-01",
      "current_release_date": "2023-02-11T23:00:00.000+00:00",
      "initial_release_date": "2023-02-11T23:00:00.000+00:00",
      "status": "final",
      "revision_history": [
        {
          "date": "2023-02-11T23:00:00.000+00:00",
          "number": "1",
          "summary": "Initial public release",
          "legacy_version": "1.0"
        },
        {
          "date": "2023-02-11T23:00:00.000+00:00",
          "number": "2",
          "summary": "Updates to products",
          "legacy_version": "2.6"
        }
      ],
      "version": "2",
      "generator": {
        "date": "2023-04-14T11:20:25.734+00:00",
        "engine": {
          "name": "ACME-Converter",
          "version": "1.0.0"
        }
      }
    }
  }
}

Also, changing the version to 1 should throw the version does not match latest revision error but does not.

Indicate Node version

Is it relevant which node version (e.g. 14 or 16) is used to run that tests? Is there a minimal version? If so, please indicate in the Readme.

Test 6.2.20 is currently missing

I'm not sure yet how to implement this properly since the strict schema check is already in and checks other aspects as well. So I'm not sure if the naive idea of "labeling the strict schema as 'optional test 6.2.20'" is appropriate here. But I guess that we can discuss this in one of our status meetings.

CSAFajv

Why do we use an AJV with strict=false for the schema validation? Shouldn't the strict_schema be checked with the strict option?

@domachine: Something to discuss in the meeting.

Invalid CPE regular expression

Running the validation for csaf_2_0 and csaf_2_0_strict, I get the following error:

"csaf_2_0: must match pattern \"^(cpe:2\\.3:[aho\\*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#\\$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|\\}~]))+(\\?*|\\*?))|[\\*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\\*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#\\$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|\\}~]))+(\\?*|\\*?))|[\\*\\-])){4})|([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\\._\\-~%]*){0,6})$\"", "csaf_2_0_strict: must match pattern \"^(cpe:2\\.3:[aho\\*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#\\$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|\\}~]))+(\\?*|\\*?))|[\\*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\\*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#\\$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|\\}~]))+(\\?*|\\*?))|[\\*\\-])){4})|([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\\._\\-~%]*){0,6})$\""

Testing again the following CPE: cpe:/o:redhat:enterprise_linux:8::fastdatapath (which seems valid to me).

Testing with another regular expression too, I get an error that the regular expression is invalid for ECMAScript. Caused by two missing escape backslashes:

Diff:

- ^(cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#\$%&'\(\)\+,/:;<=>@\[\]\^`\{\|\}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#\$%&'\(\)\+,/:;<=>@\[\]\^`\{\|\}~]))+(\?*|\*?))|[\*\-])){4})|([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\._\-~%]*){0,6})$"", "csaf_2_0_strict: must match pattern "^(cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#\$%&'\(\)\+,/:;<=>@\[\]\^`\{\|\}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#\$%&'\(\)\+,/:;<=>@\[\]\^`\{\|\}~]))+(\?*|\*?))|[\*\-])){4})|([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\._\-~%]*){0,6})$
+ ^(cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#\$%&'\(\)\+,/:;<=>@\[\]\^`\{\|\}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#\$%&'\(\)\+,/:;<=>@\[\]\^`\{\|\}~]))+(\?*|\*?))|[\*\-])){4})|([c][pP][eE]:\/[AHOaho]?(:[A-Za-z0-9\._\-~%]*){0,6})$"", "csaf_2_0_strict: must match pattern "^(cpe:2\.3:[aho\*\-](:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#\$%&'\(\)\+,/:;<=>@\[\]\^`\{\|\}~]))+(\?*|\*?))|[\*\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\*\-]))(:(((\?*|\*?)([a-zA-Z0-9\-\._]|(\\[\\\*\?!"#\$%&'\(\)\+,/:;<=>@\[\]\^`\{\|\}~]))+(\?*|\*?))|[\*\-])){4})|([c][pP][eE]:\/[AHOaho]?(:[A-Za-z0-9\._\-~%]*){0,6})$

image

Validation of document.version and revsion.number

If a document contains a pre-release-version (like 1.0.1-1.0) in document.version and a matching revision_history entry with the same content in revision_history[].number, there are two warnings:
revision_history[].number -> contains prerelease part
document.version -> version does not match latest revision

{ "document": { "category": "csaf_base", "csaf_version": "2.0", "publisher": { "category": "discoverer", "name": "test", "namespace": "http://example.test" }, "title": "test_1", "tracking": { "current_release_date": "2022-01-01T00:00:00.000Z", "id": "111", "initial_release_date": "2022-01-01T00:00:00.000Z", "revision_history": [ { "date": "2022-01-01T00:00:00.000Z", "number": "1.0.0", "summary": "\"Initial Publication\"" }, { "date": "2022-01-01T00:00:00.001Z", "number": "1.0.1-1.0", "summary": "New Version" } ], "status": "draft", "version": "1.0.1-1.0" } } }

Maybe the check according the matching according the latest version with a pre-release-tag could be done against the latest revision history entry?

`npm test` fails

Currently, npm test fails for me with the following error message:

> test
> tsc -b . && mocha tests

scripts/runTest.js:12:26 - error TS2307: Cannot find module 'fs/promises' or its corresponding type declarations.

12 import { readFile } from 'fs/promises'
                            ~~~~~~~~~~~~~

scripts/runTest.js:19:42 - error TS2339: Property 'argv' does not exist on type 'typeof process'.

19 const [, , filePath, testName] = process.argv
                                            ~~~~


Found 2 errors.

What did I miss?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.