Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ee3341a
Update changelog and version after v2.1.6
invalid-email-address Mar 30, 2022
bcd5c02
Update checked-in dependencies
invalid-email-address Mar 30, 2022
bae3a3a
Merge pull request #1005 from github/mergeback/v2.1.6-to-main-28eead24
edoardopirovano Mar 30, 2022
e769c2d
Run all PR checks on v2 branch
henrymercer Mar 30, 2022
381ea36
Delete unused workflows
henrymercer Mar 30, 2022
4d339ae
Merge pull request #1009 from github/henrymercer/run-pr-checks-on-v2-…
henrymercer Mar 30, 2022
a9095ce
Avoid failure if `@types/node` is already 12.12
edoardopirovano Mar 30, 2022
894face
Merge pull request #1008 from github/edoardo/no-fail-12.12
edoardopirovano Mar 30, 2022
e83a1d4
Stop running ML-powered queries on Windows
henrymercer Mar 30, 2022
e6f3e04
Add descriptions to each test
henrymercer Mar 30, 2022
7871f0d
Update CodeQL Action from v1 to v2 in README
henrymercer Mar 30, 2022
a2949f4
Update actions/checkout from v2 to v3
henrymercer Mar 30, 2022
ea751a9
Update other Actions from v2 to v3
henrymercer Mar 30, 2022
9dcc141
Merge pull request #1010 from github/henrymercer/stop-running-ml-powe…
henrymercer Mar 30, 2022
1ea2f2d
Merge branch 'main' into henrymercer/update-actions-major-versions
henrymercer Mar 30, 2022
b0ddf36
Merge pull request #1012 from github/henrymercer/update-actions-major…
henrymercer Mar 30, 2022
57096fe
Add a PR check to validate that ML-powered queries are run correctly
henrymercer Mar 30, 2022
dc0338e
Use latest major version of actions/upload-artifact
henrymercer Mar 30, 2022
a90d8bf
Merge pull request #1011 from github/henrymercer/ml-powered-queries-p…
henrymercer Mar 31, 2022
dd6b592
Simplify ML-powered query status report definition
henrymercer Mar 31, 2022
2c03704
Allow the version of the ML-powered pack to depend on the CLI version
henrymercer Mar 31, 2022
e26813c
Run version `~0.2.0` of the ML-powered query pack for v2.8.4+ of the CLI
henrymercer Mar 31, 2022
935969c
Merge pull request #1013 from github/henrymercer/ml-powered-query-pac…
henrymercer Mar 31, 2022
8a00ed0
Fix issue with dependencies
edoardopirovano Apr 1, 2022
f090899
Merge pull request #1015 from github/edoardo/dependency-update
edoardopirovano Apr 1, 2022
43d0664
Revert usage of `--codescanning-config` flag
edoardopirovano Apr 5, 2022
9cab82f
Merge pull request #1018 from github/edoardo/revert-codescanning-config
edoardopirovano Apr 5, 2022
488f782
Update changelog for v2.1.7
invalid-email-address Apr 5, 2022
0182a2c
Merge pull request #1019 from github/update-v2.1.7-9cab82f2
edoardopirovano Apr 5, 2022
8165d30
Revert "Update version and changelog for v1.1.6"
invalid-email-address Apr 5, 2022
380041e
Revert "Update checked-in dependencies"
invalid-email-address Apr 5, 2022
f5e5590
Merge remote-tracking branch 'origin/v2' into update-v1.1.7-0182a2c7
invalid-email-address Apr 5, 2022
3d10ffe
Update version and changelog for v1.1.7
invalid-email-address Apr 5, 2022
7eac76f
Update checked-in dependencies
invalid-email-address Apr 5, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add a PR check to validate that ML-powered queries are run correctly
  • Loading branch information
henrymercer committed Mar 31, 2022
commit 57096fe795dd4d80156b5aca370361a411c788ac
119 changes: 119 additions & 0 deletions .github/workflows/__ml-powered-queries.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 67 additions & 0 deletions pr-checks/checks/ml-powered-queries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: "ML-powered queries"
description: "Tests that ML-powered queries are run with the security-extended suite and that they produce alerts on a test DB"
versions: [
# Latest release in 2.7.x series
"stable-20220120",
"cached",
"latest",
"nightly-latest",
]
# Test on all three platforms since ML-powered queries use native code
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
steps:
- uses: ./../action/init
with:
languages: javascript
queries: security-extended
source-root: ./../action/tests/ml-powered-queries-repo
tools: ${{ steps.prepare-test.outputs.tools-url }}

- uses: ./../action/analyze
with:
output: "${{ runner.temp }}/results"
upload-database: false
env:
TEST_MODE: true

- name: Upload SARIF
uses: actions/upload-artifact@v2
with:
name: ml-powered-queries-${{ matrix.os }}-${{ matrix.version }}.sarif.json
path: "${{ runner.temp }}/results/javascript.sarif"
retention-days: 7

- name: Check results
env:
IS_WINDOWS: ${{ matrix.os == 'windows-latest' }}
shell: bash
run: |
cd "$RUNNER_TEMP/results"
# We should run at least the ML-powered queries in `expected_rules`.
expected_rules="js/ml-powered/nosql-injection js/ml-powered/path-injection js/ml-powered/sql-injection js/ml-powered/xss"

for rule in ${expected_rules}; do
found_rule=$(jq --arg rule "${rule}" '[.runs[0].tool.extensions[].rules | select(. != null) |
flatten | .[].id] | any(. == $rule)' javascript.sarif)
echo "Did find rule '${rule}': ${found_rule}"
if [[ "${found_rule}" != "true" && "${IS_WINDOWS}" != "true" ]]; then
echo "Expected SARIF output to contain rule '${rule}', but found no such rule."
exit 1
elif [[ "${found_rule}" == "true" && "${IS_WINDOWS}" == "true" ]]; then
echo "Found rule '${rule}' in the SARIF output which shouldn't have been part of the analysis."
exit 1
fi
done

# We should have at least one alert from an ML-powered query.
num_alerts=$(jq '[.runs[0].results[] |
select(.properties.score != null and (.rule.id | startswith("js/ml-powered/")))] | length' \
javascript.sarif)
echo "Found ${num_alerts} alerts from ML-powered queries.";
if [[ "${num_alerts}" -eq 0 && "${IS_WINDOWS}" != "true" ]]; then
echo "Expected to find at least one alert from an ML-powered query but found ${num_alerts}."
exit 1
elif [[ "${num_alerts}" -ne 0 && "${IS_WINDOWS}" == "true" ]]; then
echo "Expected not to find any alerts from an ML-powered query but found ${num_alerts}."
exit 1
fi
21 changes: 21 additions & 0 deletions tests/ml-powered-queries-repo/add-note.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const mongoose = require('mongoose');

Logger = require('./logger').Logger;
Note = require('./models/note').Note;

(async () => {
if (process.argv.length != 5) {
Logger.log("Creates a private note. Usage: node add-note.js <token> <title> <body>")
return;
}

// Open the default mongoose connection
await mongoose.connect('mongodb://localhost:27017/notes', { useFindAndModify: false });

const [userToken, title, body] = process.argv.slice(2);
await Note.create({ title, body, userToken });

Logger.log(`Created private note with title ${title} and body ${body} belonging to user with token ${userToken}.`);

await mongoose.connection.close();
})();
68 changes: 68 additions & 0 deletions tests/ml-powered-queries-repo/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const bodyParser = require('body-parser');
const express = require('express');
const mongoose = require('mongoose');

const notesApi = require('./notes-api');
const usersApi = require('./users-api');

const addSampleData = module.exports.addSampleData = async () => {
const [userA, userB] = await User.create([
{
name: "A",
token: "tokenA"
},
{
name: "B",
token: "tokenB"
}
]);

await Note.create([
{
title: "Public note belonging to A",
body: "This is a public note belonging to A",
isPublic: true,
ownerToken: userA.token
},
{
title: "Public note belonging to B",
body: "This is a public note belonging to B",
isPublic: true,
ownerToken: userB.token
},
{
title: "Private note belonging to A",
body: "This is a private note belonging to A",
ownerToken: userA.token
},
{
title: "Private note belonging to B",
body: "This is a private note belonging to B",
ownerToken: userB.token
}
]);
}

module.exports.startApp = async () => {
// Open the default mongoose connection
await mongoose.connect('mongodb://mongo:27017/notes', { useFindAndModify: false });
// Drop contents of DB
mongoose.connection.dropDatabase();
// Add some sample data
await addSampleData();

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded());

app.get('/', async (_req, res) => {
res.send('Hello World');
});

app.use('/api/notes', notesApi.router);
app.use('/api/users', usersApi.router);

app.listen(3000);
Logger.log('Express started on port 3000');
};
7 changes: 7 additions & 0 deletions tests/ml-powered-queries-repo/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const startApp = require('./app').startApp;

Logger = require('./logger').Logger;
Note = require('./models/note').Note;
User = require('./models/user').User;

startApp();
5 changes: 5 additions & 0 deletions tests/ml-powered-queries-repo/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports.Logger = class {
log(message, ...objs) {
console.log(message, objs);
}
};
8 changes: 8 additions & 0 deletions tests/ml-powered-queries-repo/models/note.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const mongoose = require('mongoose');

module.exports.Note = mongoose.model('Note', new mongoose.Schema({
title: String,
body: String,
ownerToken: String,
isPublic: Boolean
}));
6 changes: 6 additions & 0 deletions tests/ml-powered-queries-repo/models/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const mongoose = require('mongoose');

module.exports.User = mongoose.model('User', new mongoose.Schema({
name: String,
token: String
}));
44 changes: 44 additions & 0 deletions tests/ml-powered-queries-repo/notes-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const express = require('express')

const router = module.exports.router = express.Router();

function serializeNote(note) {
return {
title: note.title,
body: note.body
};
}

router.post('/find', async (req, res) => {
const notes = await Note.find({
ownerToken: req.body.token
}).exec();
res.json({
notes: notes.map(serializeNote)
});
});

router.get('/findPublic', async (_req, res) => {
const notes = await Note.find({
isPublic: true
}).exec();
res.json({
notes: notes.map(serializeNote)
});
});

router.post('/findVisible', async (req, res) => {
const notes = await Note.find({
$or: [
{
isPublic: true
},
{
ownerToken: req.body.token
}
]
}).exec();
res.json({
notes: notes.map(serializeNote)
});
});
37 changes: 37 additions & 0 deletions tests/ml-powered-queries-repo/read-notes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const mongoose = require('mongoose');

Logger = require('./logger').Logger;
Note = require('./models/note').Note;
User = require('./models/user').User;

(async () => {
if (process.argv.length != 3) {
Logger.log("Outputs all notes visible to a user. Usage: node read-notes.js <token>")
return;
}

// Open the default mongoose connection
await mongoose.connect('mongodb://localhost:27017/notes', { useFindAndModify: false });

const ownerToken = process.argv[2];

const user = await User.findOne({
token: ownerToken
}).exec();

const notes = await Note.find({
$or: [
{ isPublic: true },
{ ownerToken }
]
}).exec();

notes.map(note => {
Logger.log("Title:" + note.title);
Logger.log("By:" + user.name);
Logger.log("Body:" + note.body);
Logger.log();
});

await mongoose.connection.close();
})();
Loading